Servlet

在之前的笔记中,我们已经初步了解了Servlet:

  1. 概念

  2. 步骤

  3. 执行原理

  4. 生命周期

  5. Servlet3.0注解配置

接下来我们深入学习一下:

  1. Servlet体系结构
  2. Servlet相关配置

Servlet的体系结构

Servlet – 接口
|
GenericServlet – 抽象类
|
HttpServlet – 抽象类

GenericServlet

GenericServlet :将Servlet接口中其他的方法做了默认实现,只将service()方法作为抽象方法

  • 这样我们将来定义Servlet类时,可以继承GenericServlet ,只需要实现service()方法即可

例子:我们定义一个Demo01类,继承GenericServlet 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package web.Servlet;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;

@WebServlet("/demo01")
public class Demo01 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("GenericServlet...Demo01");
}
}

HttpServlet

HttpServlet:对http协议的一种封装,简化操作。在操作时:

  • 定义类时继承HttpServlet
  • 复写doGet和doPost方法

HttpServlet

例子,我们创建Demo02代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package web.Servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/demo02")
public class Demo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet...");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost...");
}
}

Servlet相关配置

urlpartten:Servlet访问路径(即@WebServlet注解中写的内容)

一个Servlet可以定义多个访问路径 :@WebServlet({"/demo01", "/demo011","/demo0111"})

例子,我们将Demo01代码修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package web.Servlet;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;

@WebServlet({"/demo01", "/demo011","/demo0111"})
public class Demo01 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("GenericServlet...Demo01");
}
}

这样访问路径就可以为多个:/demo01,/demo011,/demo0111

三个路径均能访问

注意一下路径定义规则:

  • /xxx:路径匹配
  • /xxx/xxx:多层路径,目录结构
  • *.do:自定义扩展名匹配(扩展名可以修改)

例子,创建Demo04代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package web.Servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

//@WebServlet("/user/demo03") // 多层路径
//@WebServlet("/user/*") // user下所有路径均可访问
@WebServlet("*.do") // 自定义扩展名匹配
public class Demo03 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet...Demo03");
}
}

HTTP

超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII形式给出;而消息内容则具有一个类似MIME的格式。这个简单模型是早期Web成功的有功之臣,因为它使开发和部署非常地直截了当。

概念:Hyper Text Transfer Protocol 超文本传输协议

传输协议:定义了客户端和服务器端通信时,发送数据的格式

HTTP协议

特点:

  • 基于TCP/IP的高级协议
  • 默认端口号:80
  • 基于请求/响应模型的:一次请求对应一次响应
  • 无状态的:每次请求之间相互独立,不能交互数据

历史版本:

  • 1.0:每一次请求响应都会建立新的连接
  • 1.1:复用连接

视频讲解

请求消息数据格式

  • 请求消息:客户端发送给服务器端的数据
  • 响应格式:服务器端发送给客户端的数据

请求消息数据格式:

  • 请求行
  • 请求头
  • 请求空行
  • 请求体

视频讲解

请求行

  • 请求方式 请求url 请求协议/版本(如:GET /login.html HTTP/1.1

请求方式:HTTP协议有9中请求方式(菜鸟教程HTTP请求方式简单介绍)。常用的两种:

  • GET:
    • 请求参数在请求行中,在url后
    • 请求的url长度是有限制的
    • 不太安全
  • POST
    • 请求参数在请求体中
    • 请求的url长度是没有限制的
    • 相对安全

请求头

请求头:客户端浏览器告诉服务器一些信息

常见请求头:请求头值

  • User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息
    • 可以在服务器端获取该头的信息,解决浏览器的兼容性问题
  • Referer:http://localhost/login.html
    • 告诉服务器,我(当前请求)从哪里来?作用:
      • 防盗链
      • 统计工作

Referer请求头

请求空行

请求空行,就是用来分割POST请求的请求头和请求体

请求体

  • 封装POST请求消息的请求参数

响应消息数据格式

  • 请求消息:客户端发送给服务器端的数据
  • 响应格式:服务器端发送给客户端的数据

响应消息数据格式:

  • 响应行
  • 响应头
  • 响应空行
  • 响应体

视频讲解

响应行

组成:协议/版本 响应状态码 状态码描述(如:HTTP/1.1 200 OK

  • 响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态。状态码都是3位数字。分类如下:
    • 1xx:服务器就收客户端消息,但没有接受完成,等待一段时间后,发送1xx状态码
    • 2xx:成功。代表:200
    • 3xx:重定向。代表:302(重定向),304(访问缓存)
    • 4xx:客户端错误。代表例子:
      • 404(请求路径没有对应的资源)
      • 405:请求方式没有对应的doXxx方法
    • 5xx:服务器端错误。代表:500(服务器内部出现异常)

关于状态码详细文章有:HTTP 响应代码

响应头

格式:头名称: 值

常见的响应头:

  • Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式(取值有utf-8,gbk等)
  • Content-disposition:服务器告诉客户端以什么格式打开响应体数据。具体取值有:
    • in-line:默认值,在当前页面内打开
    • attachment;filename=xxx:以附件形式打开响应体。文件下载

响应空行

空行是用于分割请求数据的行,且是必须的

响应体

该响应消息的响应体是一个html文档。浏览器可以直接识别这个html文件。(而我们访问的是一个jsp文件,响应回去的是一个html文件。说明服务器将该jsp翻译成了一个html,然后再响应给浏览器)


Request

什么是request和response

request对象是服务器对浏览器请求的封装,而response是服务器对服务器响应的封装request用来取出请求信息,而response则用来添加要返回给浏览器的信息

request和response对象

request对象和response对象的原理:

  • request和response对象是由服务器创建的。我们来使用它们
  • request对象是来获取请求消息,response对象是来设置响应消息

视频讲解

Request继承体系

request对象继承体系结构:
ServletRequest – 接口
| 继承
HttpServletRequest – 接口
| 实现
org.apache.catalina.connector.RequestFacade 类(tomcat)

Request功能

视频讲解

获取请求消息数据

  • 获取请求行数据
  • 获取请求头数据
  • 获取请求体数据

获取请求行数据

例如:GET /user/demo03?name=zhangsan HTTP/1.1
方法:

  1. 获取请求方式 :GET

    • String getMethod()
  2. 获取虚拟目录:/user

    • String getContextPath()
  3. 获取Servlet路径: /demo1

    • String getServletPath()
  4. 获取get方式请求参数:name=zhangsan

    • String getQueryString()
  5. 获取请求URI(或URL):/user/demo03

    • String getRequestURI() /user/demo03 (URI)
    • StringBuffer getRequestURL() http://localhost/user/demo03(URL)
  6. 获取协议及版本:HTTP/1.1

    • String getProtocol()
  7. 获取客户机的IP地址:

    • String getRemoteAddr()

例子:我们先设置虚拟目录为:/user

然后创建RequestDemo01,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package web.Servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/requestDemo01")
public class RequestDemo01 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {


}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.获取请求方式
String method = request.getMethod();
System.out.println("请求方式:"+method);
// 2.获取虚拟目录
String contextPath = request.getContextPath();
System.out.println("虚拟目录:"+contextPath);
// 3.获取Servlet路径
String servletPath = request.getServletPath();
System.out.println("Servlet路径:"+servletPath);
// 4.获取get方式请求参数
String queryString = request.getQueryString();
System.out.println("get方式请求参数:"+queryString);
// 5.获取请求URL(或URL)
String requestURI = request.getRequestURI();
StringBuffer requestURL = request.getRequestURL();
System.out.println("请求URI:"+requestURI);
System.out.println("请求URL:"+requestURL);
// 6.获取协议及版本
String protocol = request.getProtocol();
System.out.println("协议及版本:"+protocol);
// 7.获取客户机的IP地址
String remoteAddr = request.getRemoteAddr();
System.out.println("客户机的IP地址:"+remoteAddr);
}
}

在浏览器访问:http://localhost:8080/user/requestDemo01?name=zhangsan&age=18

结果为:

这里再简单提一下URL和URI的区别:

  • **URI:**Uniform Resource Identifier 统一资源标识符
  • **URL:**Uniform Resource Location 统一资源定位符

首先,URI,是统一资源标识符,用来唯一的标识一个资源。 而URL是统一资源定位器,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。 也就是说,URI是以一种抽象的,高层次概念定义统一资源标识,而URL和URN则是具体的资源标识的方式。

URI和URL的区别

获取请求头数据

方法:

  • 获取所有的请求头名称:Enumeration<String> getHeaderNames()

  • 通过请求头的名称获取请求头的值String getHeader(String name)

例子:

我们创建RequestDemo02,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package web.request;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;

@WebServlet("/requestDemo02")
public class RequestDemo02 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取请求头数据
// 1.获取所有的请求头名称
Enumeration<String> headerNames = request.getHeaderNames();
// 2.遍历
while (headerNames.hasMoreElements()){
String name = headerNames.nextElement();
// 3.通过请求头的名称获取请求头的值
String header = request.getHeader(name);
System.out.println("请求头名称:"+header);
}
// 通过请求头的名称获取请求头的值
// 1.获取user-agent
String agent = request.getHeader("user-agent");
System.out.println("user-agent:"+agent);
// 判断浏览器
if (agent.contains("Chrome")){
System.out.println("Google");
}else if (agent.contains("FirFox")){
System.out.println("FirFox");
}
// 2.获取Referer
String referer = request.getHeader("Referer");
System.out.println("Referer:"+referer);

}
}

通过浏览器访问:http://localhost:8080/user/requestDemo02

结果为:

关于获取Referer,从而防盗链的详细讲解:视频讲解

获取请求体数据

请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
步骤:

  1. 获取流对象
    • 获取字符输入流,只能操作字符数据:BufferedReader getReader()
    • 获取字节输入流,可以操作所有类型数据:ServletInputStream getInputStream()
      (在后续上传文件案例中会具体讲到)
  2. 再从流对象中拿数据

例子:

我们先在web包下创建regist.html,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="/user/requestDemo03" method="post">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="submit" value="注册"><br>
</form>

</body>
</html>

然后在request包下创建RequestDemo03,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package web.request;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;

@WebServlet("/requestDemo03")
public class RequestDemo03 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取请求体数据
// 1.获取流对象(这里我们获取字符输入流)
BufferedReader bufferedReader = request.getReader();
// 2.读取数据
String line = null;
while ((line = bufferedReader.readLine())!=null){
System.out.println(line);
}

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}
}

在浏览器输入:http://localhost:8080/user/regist.html,然后输入用户名和密码,结果如下:

浏览器页面

浏览器点击注册后的结果,成功获取到请求体数据

目录结构为:

目录结构

request其他功能

  • 获取请求参数(GET和POST通用)
  • 请求转发
  • 共享数据
  • 获取ServletContext

获取请求参数(GET和POST通用)

获取请求参数通用方式。不论get还是post请求方式,都可以使用下列方法来获取请求参数

  • 根据参数名称获取参数值:username=zs&password=123
    • String getParameter(String name)
  • 根据参数名称获取参数值的数组:hobby=xx&hobby=game
    • String[] getParameterValues(String name)
  • 获取所有请求的参数名称:
    • Enumeration<String> getParameterNames()
  • 获取所有参数的map集合
    • Map<String,String[]> getParameterMap()

解决中文乱码问题:

  • get方式:tomcat 8 已经将get方式乱码问题解决了
  • post方式:会乱码
  • 解决:在获取参数前,设置request的编码request.setCharacterEncoding("utf-8");

例子:我们先在web包下创建regist1.html和regist2.html两个html页面,因为要验证GET和POST,所以这两个html文件除了method不同为其余均一样,这里给出regist.html的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="/user/requestDemo04" method="post">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="submit" value="注册"><br>
</form>

</body>
</html>

创建RequestDemo04代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package web.request;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Set;

@WebServlet("/requestDemo04")
public class RequestDemo04 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// POST获取请求参数
// 1.根据参数名称获取参数值
String username = request.getParameter("username");
System.out.println("POST:"+username);
// 获取所有参数的map集合
Map<String, String[]> parameterMap = request.getParameterMap();
// 遍历map(根据entry对象遍历map,具体可看有道云笔记:https://note.youdao.com/ynoteshare1/index.html?id=52c62dc6d1bbc5920249e1297c367afe&type=note)
Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();
for (Map.Entry<String, String[]> entry : entries) {
System.out.println(entry.getKey()+entry.getValue()[0]); // 注意:key是String,value是String[]
}

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// GET获取请求参数
// 1.根据参数名称获取参数值
String username = request.getParameter("username");
System.out.println("GET:"+username);
}
}

在浏览器分别访问http://localhost:8080/user/regist1.htmlhttp://localhost:8080/user/regist2.html(一个是GET一个是POST)

点击注册按钮后结果如下:

其余的方法这里就不展示了,这里有:视频讲解

请求转发

请求转发:一种在服务器内部的资源跳转方式

Request请求转发&域对象

视频讲解

步骤:

  • 通过request对象获取请求转发器对象RequestDispatcher getRequestDispatcher(String path)
  • 使用RequestDispatcher对象来进行转发forward(ServletRequest request, ServletResponse response)

请求转发特点:

  • 浏览器地址栏路径不发生变化
  • 只能转发到当前服务器内部资源中
  • 转发是一次请求(多个资源之间可以实现一次请求)

例子:我们分别创建RequestDemo05和RequestDemo06,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package web.request;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Set;

@WebServlet("/requestDemo05")
public class RequestDemo05 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("RequestDemo05被访问");
// 从/requestDemo05转发到/requestDemo06
// 1.通过request对象获取请求转发器对象
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/requestDemo06");
// 2.使用RequestDispatcher对象来进行转发
requestDispatcher.forward(request,response);
// 一般可以通过链式编程将两步合为一行代码:
// request.getRequestDispatcher("/requestDemo06").forward(request,response);

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package web.request;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Set;

@WebServlet("/requestDemo06")
public class RequestDemo06 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("RequestDemo06被访问");
}
}

在浏览器访问:http://localhost:8080/user/requestDemo05

结果如下:

共享数据

域对象:一个有作用范围的对象,可以在范围内共享数据

request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据

方法:

  • 存储数据:void setAttribute(String name,Object obj)
  • 通过键获取值:Object getAttitude(String name)
  • 通过键移除键值对:void removeAttribute(String name)

例子,在上述RequestDemo05和RequestDemo06基础上,我们修改代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package web.request;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Set;

@WebServlet("/requestDemo05")
public class RequestDemo05 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("RequestDemo05被访问");
// 存储数据到request域中
request.setAttribute("msg","success");
// 从/requestDemo05转发到/requestDemo06
request.getRequestDispatcher("/requestDemo06").forward(request,response);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package web.request;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Set;

@WebServlet("/requestDemo06")
public class RequestDemo06 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 从/requestDemo05中获取数据
Object msg = request.getAttribute("msg");
System.out.println("获取数据为:"+msg);
System.out.println("RequestDemo06被访问");
}
}

在浏览器访问:http://localhost:8080/user/requestDemo05

结果如下:

共享数据成功

获取ServletContext

方法:

  • 获取ServletContext:ervletContext getServletContext()

例子:创建RequestDemo07代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package web.request;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/requestDemo07")
public class RequestDemo07 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取ServletContext
ServletContext servletContext = request.getServletContext();
System.out.println("获取成功:"+servletContext);
}
}

在浏览器访问:http://localhost:8080/user/requestDemo07

结果如下:


Response

request对象和response对象的原理:

  • request和response对象是由服务器创建的。我们来使用它们
  • request对象是来获取请求消息,response对象是来设置响应消息

Response功能

  • 设置响应行
  • 设置响应头
  • 设置响应体

设置响应行

方法:

  • 设置状态码:setStatus(int sc)

设置响应头

方法:

  • 设置响应头:setHeader(String name, String value)
  • 简单的重定向方法:sendRedirect(String s)

设置响应体

步骤:

  1. 获取输出流。方法:
    • 字符输出流:PrintWriter getWriter()
    • 字节输出流:ServletOutputStream getOutputStream()
  2. 使用输出流,将数据输出到客户端浏览器

Response功能实现案例

这里通过几个案例来体现上述方法

  • 完成重定向
  • 服务器输出字符数据到浏览器
  • 服务器输出字节数据到浏览器
  • 验证码

完成重定向

重定向:资源跳转的方式

重定向

视频讲解

代码实现

我们分别创建ResponseDemo01和ResponseDemo02,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package response;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/demo01")
public class ResponseDemo01 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo01...");
// 完成重定向:访问/demo01,会自动跳转到/demo02
// // 1.设置状态码为302(重定向)
// response.setStatus(302);
// // 2.设置响应头location
// response.setHeader("location","/response01/demo02");
/**
* 简单的重定向方法
* */
response.sendRedirect("/response01/demo02");
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package response;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/demo02")
public class ResponseDemo02 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo02...");
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}

在浏览器中访问:http://localhost:8080/response01/demo01

结果如下:

重定向的特点

视频讲解

重定向的特点(redirect)

  • 地址栏路径发生变化
  • 重定向可以访问其他站点(服务器)的资源
  • 重定向是两次请求,不能使用request对象来共享数据

之前学习过的请求转发

转发的特点(forward)

  • 地址栏路径不变
  • 转发只能访问当前服务器下的资源
  • 转发是一次请求,可以使用request对象来共享数据

路径的写法

视频讲解

路径分类:

  1. 相对路径:通过相对路径不可以确定唯一资源(如:./index.html)
    • 以.开头的路径路径
    • 规则:找到当前资源和目标资源之间的相对位置关系
      • ./:当前目录
      • …/:后退一级目录
  2. 绝对路径:通过绝对路径可以确定唯一资源(如:http://localhost:8080/response01/demo01,简化书写为:/response01/demo01
    • 以/开头的路径
    • 规则:判断定义的路径是给谁用的?判断请求将来从哪儿发出
      • 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)。重定向就需要加虚拟目录
        • 建议虚拟目录动态获取(使用方法获取):request.getContextPath()
      • 给服务器使用:不需要加虚拟目录(比如说请求转发时就没有加虚拟路径)

服务器输出字符数据到浏览器

步骤:

  1. 获取字符输出流
  2. 输出数据

注意,乱码问题:

  • PrintWriter pw = response.getWriter();获取的流的默认编码是ISO-8859-1
  • 设置该流的默认编码
  • 告诉浏览器响应体使用的编码

解决中文乱码(简单的形式,设置编码,是在获取流之前设置):

  • response.setContentType("text/html;charset=utf-8");

例子:创建ResponseDemo03代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package response;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/demo03")
public class ResponseDemo03 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 解决中文乱码
response.setContentType("text/html;charset=utf-8");
// // 1.获取字符输出流
// PrintWriter printWriter = response.getWriter();
// // 2.输出数据
// printWriter.write("ResponseDemo03:hello,world!");
/**
* 通过链式编程简化上述代码
* */
response.getWriter().write("ResponseDemo03,你好!");
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}

在浏览器输入:http://localhost:8080/response01/demo03

结果如下:

服务器输出字节数据到浏览器

步骤:

  1. 获取字节输出流
  2. 输出数据

例子,创建ResponseDemo04,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package response;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/demo04")
public class ResponseDemo04 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 解决中文乱码
response.setContentType("text/html;charset=utf-8");
// 1.获取字节输出流
ServletOutputStream servletOutputStream = response.getOutputStream();
// 2.输出数据
servletOutputStream.write("ResponseDemo04:你好".getBytes("utf-8"));
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}

在浏览器输入:http://localhost:8080/response01/demo04

结果如下:

验证码

  • 验证码本质:图片
  • 目的:防止恶意表单注册

视频讲解

例子:先创建CheckCodeServlet,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package response;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

@WebServlet("/check")
public class CheckCodeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置验证码图片参数

int width = 100;
int height = 50;

//1.创建一对象,在内存中图片(验证码图片对象)
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);


//2.美化图片
//2.1 填充背景色
Graphics g = image.getGraphics();//画笔对象
g.setColor(Color.PINK);//设置画笔颜色
g.fillRect(0,0,width,height);

//2.2画边框
g.setColor(Color.BLUE);
g.drawRect(0,0,width - 1,height - 1);

String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789";
//生成随机角标
Random ran = new Random();

for (int i = 1; i <= 4; i++) {
int index = ran.nextInt(str.length());
//获取字符
char ch = str.charAt(index);//随机字符
//2.3写验证码
g.drawString(ch+"",width/5*i,height/2);
}

//2.4画干扰线
g.setColor(Color.GREEN);

//随机生成坐标点

for (int i = 0; i < 10; i++) {
int x1 = ran.nextInt(width);
int x2 = ran.nextInt(width);

int y1 = ran.nextInt(height);
int y2 = ran.nextInt(height);
g.drawLine(x1,y1,x2,y2);
}

//3.将图片输出到页面展示
ImageIO.write(image,"jpg",response.getOutputStream());
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}

然后在web包下创建register.html,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>验证码</title>

<script>
/*
分析:
点击超链接或者图片,需要换一张
1.给超链接和图片绑定单击事件
2.重新设置图片的src属性值
*/
window.onload = function(){
//1.获取图片对象
var img = document.getElementById("checkCode");
//2.绑定单击事件
img.onclick = function(){
//加时间戳
var date = new Date().getTime();
img.src = "/response01/check?"+date;
}
}
</script>
</head>

<body>
<img id="checkCode" src="/response01/check" />
<a id="change" href="">看不清换一张?</a>
</body>
</html>

在浏览器输入:http://localhost:8080/response01/register.html

结果如下:

验证码动态刷新


ServletContext对象

servletContext接口是Servlet中最大的一个接口,呈现了web应用的Servlet视图。ServletContext实例是通过 getServletContext()方法获得的,由于HttpServlet继承GenericServlet的关系,GenericServlet类和HttpServlet类同时具有该方法。

WEB容器在启动时,它会为每个Web应用程序都创建一个对应的ServletContext,它代表当前Web应用。并且它被所有客户端共享。

ServletContext对象可以通过ServletConfig.getServletContext()方法获得对ServletContext对象的引用,也可以通过this.getServletContext()方法获得其对象的引用。

由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。公共聊天室就会用到它。

当web应用关闭、Tomcat关闭或者Web应用reload的时候,ServletContext对象会被销毁

ServletContext 对象

概念:代表整个web应用,可以和程序的容器(服务器)来通信

获取ServletContext对象

获取ServletContext对象的方法:

  • 通过request对象获取:request.getServletContext();
  • 通过HttpServlet获取:this.getServletContext();

例子:创建ServletContextDemo01代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package servletcontext;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/context01")
public class ServletContextDemo01 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取ServletContext对象
ServletContext servletContext1 = request.getServletContext(); // 通过request对象获取
ServletContext servletContext2 = this.getServletContext(); // 通过HttpServlet获取
System.out.println(servletContext1);
System.out.println(servletContext2);
System.out.println(servletContext1 == servletContext2); // 结果为true
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}

在浏览器中访问:http://localhost:8080/response01/context01

结果如下:

ServletContext对象功能

  • 获取MIME类型
  • 共享数据
  • 获取文件的真实(服务器)路径

获取MIME类型

媒体类型(通常称为 Multipurpose Internet Mail Extensions 或 MIME 类型 )是一种标准,用来表示文档、文件或字节流的性质和格式。

浏览器通常使用MIME类型(而不是文件扩展名)来确定如何处理URL,因此Web服务器在响应头中添加正确的MIME类型非常重要。如果配置不正确,浏览器可能会曲解文件内容,网站将无法正常工作,并且下载的文件也会被错误处理。

MIME 类型

视频讲解

MIME类型:在互联网通信过程中定义的一种文件数据类型。

格式: 大类型/小类型。比如:text/htmlimage/jpeg

获取MIME类型:

  • String getMimeType(String file)

例子:创建ServletContextDemo02代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package servletcontext;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/context02")
public class ServletContextDemo02 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取MIME类型
// 1.先获取ServletContext对象
ServletContext servletContext = this.getServletContext(); // 通过HttpServlet获取
// 2.定义文件名称
String fileName = "hello.jpg";
// 3.获取MIME类型
String mimeType = servletContext.getMimeType(fileName);
System.out.println(mimeType);
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}

在浏览器中访问:http://localhost:8080/response01/context02

结果如下:

共享数据

(谨慎使用,一是不安全,二是内存占用时间过长,生命周期过长)

ServletContext是一个域对象(域对象:一个有作用范围的对象,可以在范围内共享数据)

注意,ServletContext对象范围:所有用户所有请求的数据(上文的request域,代表一次请求的范围)

关于共享数据,上文也有详细说明

方法:

  • 存储数据:void setAttribute(String name,Object obj)
  • 通过键获取值:Object getAttitude(String name)
  • 通过键移除键值对:void removeAttribute(String name)

例子:我们分别创建ServletContextDemo03和ServletContextDemo04,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package servletcontext;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/context03")
public class ServletContextDemo03 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.先获取ServletContext对象
ServletContext servletContext = this.getServletContext(); // 通过HttpServlet获取
// 2.存储数据
servletContext.setAttribute("msg","hello");
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package servletcontext;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/context04")
public class ServletContextDemo04 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.先获取ServletContext对象
ServletContext servletContext = this.getServletContext(); // 通过HttpServlet获取
// 2.获取数据
Object msg = servletContext.getAttribute("msg");
System.out.println(msg);
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}

在两个不同的浏览器分别访问:http://localhost:8080/response01/context03http://localhost:8080/response01/context04

结果如下:

这说明ServletContext对象可以在不同用户之间共享数据

获取文件的真实(服务器)路径

视频讲解

获取文件的真实(服务器)路径方法:

  • String getRealPath(String path)

例子:我们要获取hello.txt,world.txt,abc.txt这三个文件的路径。这三个文件位置如下:

创建ServletContextDemo05,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package servletcontext;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/context05")
public class ServletContextDemo05 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.先获取ServletContext对象
ServletContext servletContext = this.getServletContext(); // 通过HttpServlet获取

// 2.获取文件的真实(服务器)路径方法
// 2-1 获取web目录下的资源路径(world.txt)
String realPath_world = servletContext.getRealPath("/world.txt");
System.out.println("world.txt路径:"+realPath_world);
// 2-2 获取WEB-INF目录下的资源(hello.txt)
String realPath_hello = servletContext.getRealPath("/WEB-INF/hello.txt");
System.out.println("hello.txt路径:"+realPath_hello);
// 2-3 获取src目录下的资源路径(abc.txt)
String realPath_abc = servletContext.getRealPath("/WEB-INF/classes/abc.txt");
System.out.println("abc.txt路径:"+realPath_abc);
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}

在浏览器中访问:http://localhost:8080/response01/context05

结果如下:


案例

登录案例

本案例仓库地址:https://gitee.com/qingyu1011/springboot_study/tree/master/J2EE/Demo_Login

知识点:Request+JDBC

用户登录案例需求:

  1. 编写login.html登录页面
    • username & password 两个输入框
  2. 使用Druid数据库连接池技术,操作mysql,jdbc_study数据库中user表
  3. 使用JdbcTemplate技术封装JDBC
  4. 登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您
  5. 登录失败跳转到FailServlet展示:登录失败,用户名或密码错误

案例分析:视频讲解

登录案例分析

实现步骤:

1.创建项目,导入html登录页面和druid配置文件,以及相关jar包

创建login.html,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/demo01/login" method="post">
用户名:<input type="text" name="username"> <br>
密码:<input type="password" name="password"><br>

<input type="submit" value="登录">

</form>
</body>
</html>

注意form表单中的action路径写法:虚拟路径+Servlet的资源路径

创建druid.properties配置文件放在src目录下,代码如下:

1
2
3
4
5
6
7
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc_study
username=root
password=123456
initialSize=5
maxActive=10
maxWait=3000

2.创建数据库环境

user表中数据如下:

user表数据

3.在domain包下创建User类,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package edu.qing.domain;

// 用户User的JavaBean实体类

public class User {
private Integer id;
private String username;
private String password;

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
// 省略Getters和Setters
}

4.在util包下,创建编写工具类JDBCUtils代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package edu.qing.util;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JDBCUtils {
// 定义成员变量
private static DataSource dataSource;
// 提供静态代码块加载配置文件,初始化连接池对象
static {
try {
// 1.加载配置文件
Properties properties = new Properties();
properties.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
// 2.获取DataSource
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 获取连接方法getConnection
* */
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}

/**
* 获取连接池方法
* */
public static DataSource getDataSource(){
return dataSource;
}


}

5.在dao包下,创建类UserDao,提供login方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package edu.qing.dao;

import edu.qing.domain.User;
import edu.qing.util.JDBCUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;

public class UserDao {
// 声明JDBCTemplate对象公用
private JdbcTemplate jdbcTemplate = new JdbcTemplate(JDBCUtils.getDataSource());


/**
* 登录方法
* @param loginUser User类,只需要用户名和密码
* @return user包含用户的全部信息(三个字段),return null表示没有查询到
*/
public User login(User loginUser){
try {
String sql = "select * from user where username = ? and password = ?";
// 调用JDBCTemplate的方法来完成查询(别忘了给?赋值,即在query中添加参数)
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class),
loginUser.getUsername(), loginUser.getPassword());
return user;
} catch (DataAccessException e) {
e.printStackTrace(); // 记录日志
return null;
}
}
}

6.接下来在servlet包下创建LoginServlet,FailServlet,SuccessServlet代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package edu.qing.servlet;

import edu.qing.dao.UserDao;
import edu.qing.domain.User;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.设置编码
request.setCharacterEncoding("utf-8");
// 2.获取请求参数
String username = request.getParameter("username");
String password = request.getParameter("password");
// 3.封装User对象(因为login方法需要User1对象作参数)
User loginUser = new User();
loginUser.setUsername(username);
loginUser.setPassword(password);
// 4.调用UserDao的login方法
UserDao userDao = new UserDao();
User user = userDao.login(loginUser);
// 5.判断user
if (user == null){ // 登录失败(转到请求到/fail)
request.getRequestDispatcher("/fail").forward(request,response);
} else { // 登录成功(将用户信息存储到request域,并转发到/success)
request.setAttribute("msg",user);
request.getRequestDispatcher("/success").forward(request,response);
}
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package edu.qing.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/fail")
public class FailServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置编码
response.setContentType("text/html;charset=utf-8");
// 在页面提示失败信息
response.getWriter().write("登录失败!请检查用户名或密码");
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package edu.qing.servlet;

import edu.qing.domain.User;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/success")
public class SuccessServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取request域中的信息
User user = (User) request.getAttribute("msg");
// 设置编码
response.setContentType("text/html;charset=utf-8");
if (user !=null){ // 防止空指针异常
// 在页面提示成功信息
response.getWriter().write("登录成功!欢迎您:"+ user.getUsername());
}
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}

在浏览器访问:http://localhost:8080/demo01/login.html

结果如下:

登录页面

登录成功

登录失败

BeanUtils工具类,简化数据封装

BeanUtils工具类,用于封装JavaBean的。

这里简单提一下JavaBean。JavaBean:标准的Java类。

要求:

  • 类必须被public修饰
  • 必须提供空参的构造器
  • 成员变量必须使用private修饰
  • 提供公共setter和getter方法

功能:封装数据

了解一些概念:

  • 成员变量,很好理解,比如说User类的id,就是成员变量
  • 属性:setter和getter方法截取后的产物
    • 例如:getUsername() --> Username–> username

关于属性的视频讲解

BeanUtils工具类的方法:

  • 设置属性值:setProperty()
  • 获取属性值:getProperty()
  • 将map集合的键值对信息,封装到对应的JavaBean对象populate(Object obj, Map map)

在上述代码中,我们封装JavaBean是这样操作的:在LoginServlet中编写:

1
2
3
4
5
6
7
// 2.获取请求参数
String username = request.getParameter("username");
String password = request.getParameter("password");
// 3.封装User对象(因为login方法需要User1对象作参数)
User loginUser = new User();
loginUser.setUsername(username);
loginUser.setPassword(password);

这样的话如果需要封装的请求参数过多,就会导致代码很麻烦。这时就可以使用BeanUtils工具类,来简化数据封装

1.首先导入jar包:commons-beanutils-1.8.0.jar

2.在LoginServlet中修改上述封装代码,改为以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 2.获取请求参数
Map<String, String[]> parameterMap = request.getParameterMap();
// 3.封装User对象(使用BeanUtils工具类,简化数据封装)(因为login方法需要User1对象作参数)
// 3-1 创建空的User对象
User loginUser = new User();
// 3-2 使用BeanUtils封装
try {
BeanUtils.populate(loginUser,parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}

文件下载案例

知识点:综合整个笔记

文件下载案例需求:

  • 页面显示超链接
  • 点击超链接后弹出下载提示框
  • 完成图片文件下载

案例分析:(视频讲解

  • 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求
  • 任何资源都必须弹出下载提示框
  • 使用响应头设置资源的打开方式:content-disposition:attachment;filename=xxx

步骤:

  1. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename
  2. 定义Servlet,要实现以下功能:
    • 获取文件名称
    • 使用字节输入流加载文件进内存
    • 指定response的响应头: content-disposition:attachment;filename=xxx
    • 将数据写出到response输出流

代码实现:

首先创建download.html文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>下载</title>
</head>
<body>
<a>从浏览器中查看</a>
<hr/>
<a href="/response01/imgs/top1.jpg">图片1</a>
<hr/>
<a href="/response01/imgs/top2.jpg">图片2</a>

<hr/>
<!--指向Servlet,传递资源名称filename-->
<a>点击下载</a>
<hr/>
<a href="/response01/download?filename=top1.jpg">图片1</a>
<hr/>
<a href="/response01/download?filename=top2.jpg">图片2</a>

</body>
</html>

然后在download包下创建DownloadServlet代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package download;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;

@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.获取文件名称(参数名从html文件通过参数传递,所以这里我们来获取参数)
String filename = request.getParameter("filename");

// 2.使用字节输入流加载文件进内存
// 2-1 找到文件的真实路径
ServletContext servletContext = this.getServletContext(); // 获取ServletContext对象
String realPath = servletContext.getRealPath("/imgs/"+filename);
// 2-2 用字节流关联
FileInputStream fileInputStream = new FileInputStream(realPath);

// 3.指定response的响应头: content-disposition:attachment;filename=xxx
// 3-1 设置本次响应体数据格式以及编码格式(Content-Type)
String mimeType = servletContext.getMimeType(filename); // 获取对应文件的mime类型
response.setHeader("Content-Type",mimeType);
// 3-2 设置响应头类型打开方式(将Content-disposition设置为attachment;filename=xxx)
response.setHeader("Content-disposition","attachment;filename="+filename);

// 4.将字节数据写出到response输出流
ServletOutputStream servletOutputStream = response.getOutputStream();
byte[] bytes = new byte[1024 * 8]; // 字节数组作为缓冲区
int length = 0; // 当前读取个数
while ((length = fileInputStream.read(bytes)) != -1){ // 没有读到文件末尾
servletOutputStream.write(bytes,0,length);
}
fileInputStream.close(); // 关闭输入流
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}

在浏览器输入:http://localhost:8080/response01/download.html

结果如下:

文件下载成功

中文文件名问题

视频讲解

解决思路:

  1. 获取客户端使用的浏览器版本信息
  2. 根据不同的版本信息,设置filename的编码方式不同

具体例子:首先在utils包下创建DownLoadUtils工具类代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package utils;

import sun.misc.BASE64Encoder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;


public class DownLoadUtils {

public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}

然后在DownloadServlet中,补充相关代码(在// 3-2 设置响应头类型打开方式后补充):

1
2
3
4
5
6
7
8
9
// 3-2 设置响应头类型打开方式(将Content-disposition设置为attachment;filename=xxx)
/**
* 解决中文文件名
* */
// 1.获取user-agent请求头
String header = request.getHeader("user-agent");
// 2.使用工具类方法重新编码文件名即可
filename = DownLoadUtils.getFileName(header, filename);
response.setHeader("Content-disposition","attachment;filename="+filename);