HttpServlet 简介
HttpServlet是对HTTP协议封装的Servlet实现类。
Servlet的体系结构:
在开发中,关注更多的是Servlet.service()方法。而每实现一个Servlet就必须实现Servlet接口,重写接口中的5个方法。
但其实可以通过继承HttpServlet来编写Servlet,简化Servlet的开发流程。并且,如果是开发B/S架构的Web项目,针对的都是HTTP协议。
使用HttpServlet的格式如下:
1package com.linner.web;
2
3import javax.servlet.ServletException;
4import javax.servlet.annotation.WebServlet;
5import javax.servlet.http.*;
6import java.io.IOException;
7
8@WebServlet("/demo")
9public class ServletDemo extends HttpServlet {
10
11 // 需要复写以下两个方法:
12
13 @Override
14 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
15 System.out.println("Get...");
16 }
17
18 @Override
19 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
20 System.out.println("Post...");
21 }
22}
-
启动Tomcat,使用浏览器访问http://localhost:8080/web-demo/demo可以在控制台看到
doGet()方法被执行。 -
在项目的
webapp目录下新建index.html,写入:1<!DOCTYPE html> 2<html lang="zh"> 3<head> 4 <meta charset="UTF-8"> 5 <title>TestPost</title> 6</head> 7<body> 8 <form action="/web-demo/demo" method="post"> 9 <input name="username"/><input type="submit"/> 10 </form> 11</body> 12</html>启动Tomcat,访问http://localhost:8080/web-demo/,在表单输入内容后提交。即可在控制台看到
doPost()被执行。
使用Servlet实现HttpServlet
既然HttpServlet继承自Servlet,那么也可以通过编写Servlet类来实现HttpServlet:
1package com.linner.web;
2
3import javax.servlet.*;
4import javax.servlet.http.HttpServletRequest;
5import java.io.IOException;
6
7public class MyHttpServlet implements Servlet {
8
9 @Override
10 public void init(ServletConfig servletConfig) throws ServletException {
11
12 }
13
14 @Override
15 public ServletConfig getServletConfig() {
16 return null;
17 }
18
19 @Override
20 public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
21 // 根据请求方式的不同,分别进行处理
22
23 HttpServletRequest request = (HttpServletRequest) servletRequest;
24 // 1. 获取请求方式
25 String method = request.getMethod();
26
27 // 2. 判断
28 if ("GET".equals(method)) {
29 // GET 方式的处理逻辑
30 doGet(servletRequest, servletResponse);
31 } else if ("POST".equals(method)) {
32 // POST 方式的处理逻辑
33 doPost(servletRequest, servletResponse);
34 }
35 }
36
37 protected void doPost(ServletRequest servletRequest, ServletResponse servletResponse) {
38 }
39
40 protected void doGet(ServletRequest servletRequest, ServletResponse servletResponse) {
41 }
42
43 @Override
44 public String getServletInfo() {
45 return null;
46 }
47
48 @Override
49 public void destroy() {
50
51 }
52}
翻阅
HttpServlet.service()方法源码,可以发现HttpServlet不仅仅可以对GET和POST进行处理,还能处理其它五种请求:doHead()、doPut()、doDelete()、doOptions()和doTrace()。
HttpServletRequest 和 HttpServletResponse
Request 和 Response 概述
Request是请求对象,Response是响应对象。在Servlet中也存在这样的两个对象:
1public class ServletDemo implements Servlet {
2
3 @Override
4 public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
5 // ...
6 }
7
8 // ......
9
10}
Request作用:获取请求数据
-
浏览器会发送HTTP请求到后台服务器(如,Tomcat)
-
HTTP的请求中会包含很多请求数据
如,HTTP协议请求:
- 请求行
- 请求头
- 请求体
-
后台服务器会对HTTP请求中的数据进行解析并把解析结果存入到一个对象中
所存入的对象即为
Request对象,所以我们可以从Request对象中获取请求的相关参数 -
获取到数据后就可以继续后续的业务
如,获取用户名和密码就可以实现登录操作的相关业务
Response作用:设置响应数据
- 业务处理完后,后台就需要给前端返回业务处理的结果(即,响应数据)
- 把响应数据封装到
Response对象中 - 后台服务器会解析
Response对象,按照格式(响应行+响应头+响应体)拼接结果 - 浏览器最终解析结果,把内容展示在浏览器给用户浏览
而HttpServlet使用的 Request 和 Response 对象与Servlet有所不同。HttpServlet使用的是 HttpServletRequest 和 HttpServletResponse。
Example:
1@WebServlet("/demo")
2public class HttpServletDemo extends HttpServlet {
3 @Override
4 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
5 // 使用request对象 获取请求数据
6 String name = request.getParameter("name");
7 // Parameter在地址中以 ? 开始:url?name=zhangsan
8
9 // 使用response对象 设置响应数据
10 response.setHeader("content-type","text/html;charset=utf-8");
11 response.getWriter().write("<h1>"+name+",欢迎您!</h1>");
12 }
13
14 @Override
15 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
16 this.doGet(request, response);
17 }
18}
Request 和 Response 继承体系
HttpServletRequest和ServletRequest之间是继承关系,HttpServletResponse和ServletResponse是继承关系。
Request之间的继承关系如下:
Response之间的继承关系如下:
HttpServletRequest
HTTP常用的请求方式为:
- GET
- POST
HTTP请求数据总共分为三部分内容:
- 请求行
- 请求头
- 请求体
在请求数据中,还包含着请求参数:
- 对于GET:请求参数包含在请求头中。
- 对于POST:请求参数一般包含在请求体中。
获取请求行数据
如打开以下链接:
1http://localhost:8080/HttpServlet/httpservlet.html?username=linner
其请求行大致内容如下:
1GET /HttpServlet/httpservlet.html?username=linner HTTP/1.1
包含以下三部分内容:
-
请求方式:
GET -
请求资源路径:
/HttpServlet/httpservlet.html?username=linner请求资源路径包含:
- 虚拟目录(项目访问路径):
/HttpServlet - URI(统一资源标识符):
/HttpServlet/httpservlet.html - 请求参数:
username=linner
- 虚拟目录(项目访问路径):
-
HTTP协议及版本:
HTTP/1.1
这三部分内容,HttpServletRequest对象都提供了对应的API方法来获取:
-
获取请求方式:
1String getMethod()返回:
GET -
获取虚拟目录(项目访问路径):
1String getContextPath()返回:
/HttpServlet -
获取URL(统一资源定位符):
1StringBuffer getRequestURL()返回:
http://localhost:8080/HttpServlet/httpservlet.htm -
获取URI(统一资源标识符):
1String getRequestURI()返回:
/HttpServlet/httpservlet.html -
获取请求参数(GET方式):
1String getQueryString()返回:
username=linner(多个参数也一并返回)
Example:
1package com.linner.web;
2
3import javax.servlet.*;
4import javax.servlet.http.*;
5import javax.servlet.annotation.*;
6import java.io.IOException;
7
8@WebServlet("/test")
9public class TestHttpServlet extends HttpServlet {
10 @Override
11 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
12 System.out.println("---------------------------------------");
13 System.out.println("请求行:");
14 System.out.println("请求方式:" + request.getMethod());
15 System.out.println("虚拟目录:" + request.getContextPath());
16 System.out.println("URL:" + request.getRequestURL());
17 System.out.println("URI:" + request.getRequestURI());
18 System.out.println("请求参数:" + request.getQueryString());
19 System.out.println("---------------------------------------");
20 }
21
22 @Override
23 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
24 this.doGet(request, response);
25 }
26}
获取请求头数据
请求头数据由多个 key: value 组成,如客户端浏览器的版本信息:
1User-Agent: Mozila/5.0 Chrome/105.0.0.0 Edg/105.0.1343.42
HttpServletRequest 获取请求头的方法为:
1String getHeader(String name)
name:是请求头中的key。- 返回值:返回
name对应key的value。
使用getHeader()获取客户端浏览器的版本信息:
1package com.linner.web.request;
2
3import javax.servlet.ServletException;
4import javax.servlet.annotation.WebServlet;
5import javax.servlet.http.HttpServlet;
6import javax.servlet.http.HttpServletRequest;
7import javax.servlet.http.HttpServletResponse;
8import java.io.IOException;
9
10@WebServlet("/test")
11public class RequestDemo7 extends HttpServlet {
12
13 @Override
14 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
15 String agent = request.getHeader("user-agent"); // name 不区分大小写
16 System.out.println(agent);
17 }
18
19 @Override
20 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
21 this.doGet(request, response);
22 }
23}
输出:
1Mozila/5.0 Chrome/105.0.0.0 Edg/105.0.1343.42
获取请求体数据
浏览器发送的GET请求,是没有请求体的。只有在发送POST请求时才带有请求体。
请求体中的数据格式如:
1username=linner&password=123456
与资源路径中,请求参数的格式一样。
HttpServletRequest提供了两种方式来获取请求体中的数据:
-
获取字节输入流:
当前端发送的是字节数据,如传递的是文件数据时使用。
1ServletInputStream getInputStream() -
获取字符输入流:
当前端发送的是纯文本数据时使用。
1BufferedReader getReader()
如果要在客户端浏览器发送POST请求,需要编写一个
<form>表单。
Example:
-
在项目的
webapp目录下添加index.html:1<!DOCTYPE html> 2<html lang="zh"> 3<head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6</head> 7<body> 8 <!-- 9 action: 表单提交的请求地址 10 method: 请求方式,指定为post 11 --> 12 <form action="/request-demo/test" method="post"> 13 <input type="text" name="username"> 14 <input type="password" name="password"> 15 <input type="submit"> 16 </form> 17</body> 18</html> -
在
doPost方法中获取数据:由于
index.html提交的是纯文本数据,所以要使用getReader()方法获取。1package com.linner.web.request; 2 3import javax.servlet.ServletException; 4import javax.servlet.annotation.WebServlet; 5import javax.servlet.http.HttpServlet; 6import javax.servlet.http.HttpServletRequest; 7import javax.servlet.http.HttpServletResponse; 8import java.io.BufferedReader; 9import java.io.IOException; 10 11@WebServlet("/test") 12public class RequestDemo8 extends HttpServlet { 13 14 @Override 15 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 16 17 } 18 19 @Override 20 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 21 // 1. 获取字符输入流 22 BufferedReader br = request.getReader(); 23 // 2. 读取数据 24 String line = br.readLine(); 25 System.out.println(line); 26 } 27}BufferedReader流是通过HttpServletRequest对象来获取的,当请求完成后HttpServletRequest对象就会被销毁,HttpServletRequest对象被销毁后,BufferedReader流就会自动关闭,所以就不需要手动关闭流了。getReader()获取请求参数后,还需要使用readLine()读取参数数据。 -
通过浏览器访问:http://localhost:8080/request-demo/。在表单中输入内容,然后提交,就可以在控制台看到前端所发送的请求数据:
1username=linner&password=123456
获取请求参数
使用getQueryString()方法和getReader()分别获取GET和POST的请求参数:
1package com.linner.web;
2
3import javax.servlet.ServletException;
4import javax.servlet.annotation.WebServlet;
5import javax.servlet.http.HttpServlet;
6import javax.servlet.http.HttpServletRequest;
7import javax.servlet.http.HttpServletResponse;
8import java.io.IOException;
9
10@WebServlet("/test")
11public class RequestDemo extends HttpServlet {
12 @Override
13 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
14 String result = req.getQueryString();
15 System.out.println(result);
16
17 }
18 @Override
19 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
20 BufferedReader br = req.getReader();
21 String result = br.readLine();
22 System.out.println(result);
23 }
24}
上述代码存在的问题:
-
doGet()和doPost中出现了重复代码。在实际业务中,可能会出现很多相同的业务代码。
-
doGet()和doPost都必须存在。 -
GET请求和POST请求获取请求参数的方式不一样。
-
在
doPost()中调用doGet(),然后在doGet()判断请求的方式,并分别做处理:1package com.linner.web; 2 3import javax.servlet.ServletException; 4import javax.servlet.annotation.WebServlet; 5import javax.servlet.http.HttpServlet; 6import javax.servlet.http.HttpServletRequest; 7import javax.servlet.http.HttpServletResponse; 8import java.io.IOException; 9 10@WebServlet("/test") 11public class RequestDemo extends HttpServlet { 12 @Override 13 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 14 // 1. 获取请求方式 15 String method = req.getMethod(); 16 // 2. 获取请求参数 17 String params = ""; 18 if("GET".equals(method)){ 19 params = req.getQueryString(); 20 }else if("POST".equals(method)){ 21 BufferedReader reader = req.getReader(); 22 params = reader.readLine(); 23 } 24 // 3. 处理请求 25 System.out.println(params); 26 27 } 28 @Override 29 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 30 this.doGet(req,resp); 31 } 32} -
HttpServletRequest已经对获取请求参数的方式进行了封装:-
获取所有参数Map集合
1Map<String, String[]> getParameterMap() -
根据名称获取参数值(返回值为数组,返回多个参数)
1String[] getParameterValues(String name) -
根据名称获取参数值(单个值)
1String getParameter(String name)
同样是在
doPost()中调用doGet(),然后在doGet()处理参数,但是在获取参数时不用对请求方式进行判断。Example:
-
webapp/index.html:1<!DOCTYPE html> 2<html lang="zh"> 3<head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6</head> 7<body> 8 test:get 9 <form action="/request-demo/test" method="get"> 10 <input type="text" name="username"><br> 11 <input type="password" name="password"><br> 12 <input type="checkbox" name="hobby" value="1"> get-1 13 <input type="checkbox" name="hobby" value="2"> get-2 <br> 14 <input type="submit"> 15 </form> 16 test:post 17 <form action="/request-demo/test" method="post"> 18 <input type="text" name="username"><br> 19 <input type="password" name="password"><br> 20 <input type="checkbox" name="hobby" value="1"> post-1 21 <input type="checkbox" name="hobby" value="2"> post-2 <br> 22 <input type="submit"> 23 </form> 24</body> 25</html> -
使用
getParameterValues()和getParameter()获取请求参数:1package com.linner.web; 2 3import javax.servlet.ServletException; 4import javax.servlet.annotation.WebServlet; 5import javax.servlet.http.HttpServlet; 6import javax.servlet.http.HttpServletRequest; 7import javax.servlet.http.HttpServletResponse; 8import java.io.IOException; 9 10@WebServlet("/test1") 11public class RequestDemo1 extends HttpServlet { 12 @Override 13 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 14 System.out.println("---------------"); 15 // 验证请求的方式: 16 String method = req.getMethod(); 17 System.out.println(method); 18 19 // 获取多个 value: 20 System.out.print("hobby: "); 21 String[] hobbies = request.getParameterValues("hobby"); 22 for (String hobby : hobbies) { 23 System.out.print(hobby + ", "); 24 } 25 System.out.println("\b\b "); 26 27 // 获取单个 value: 28 String username = request.getParameter("username"); 29 String password = request.getParameter("password"); 30 System.out.println("username: " + username); 31 System.out.println("password: " + password); 32 } 33 34 @Override 35 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 36 this.doGet(request, response); 37 } 38} -
使用
getParameterMap()一次性获取所有参数:1package com.linner.web; 2 3import javax.servlet.ServletException; 4import javax.servlet.annotation.WebServlet; 5import javax.servlet.http.HttpServlet; 6import javax.servlet.http.HttpServletRequest; 7import javax.servlet.http.HttpServletResponse; 8import java.io.IOException; 9import java.util.Map; 10 11@WebServlet("/test") 12public class RequestDemo extends HttpServlet { 13 @Override 14 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 15 System.out.println("-------------------------------------------"); 16 // 验证请求的方式: 17 String method = request.getMethod(); 18 System.out.println(method); 19 20 // 获取所有参数的Map集合 21 Map<String, String[]> map = request.getParameterMap(); 22 for (String key : map.keySet()) { 23 System.out.print(key + ":"); 24 25 // 获取key对应的所有values 26 String[] values = map.get(key); 27 for (String value : values) { 28 System.out.print(value + ", "); 29 } 30 System.out.println("\b\b "); 31 } 32 } 33 34 @Override 35 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 36 this.doGet(request, response); 37 } 38}
-
请求转发
请求转发(forward)是一种在服务器内部的资源跳转方式。如:
- 服务器内资源A获取请求。
- 资源A(处理一部分数据后)将请求转发给资源B去处理。
- 资源B处理完成后将将结果响应给浏览器。
请求从资源A到资源B的过程即为请求转发。
请求转发的特点:
- 浏览器地址栏路径不变。
- 只能转发到当前服务器的内部资源。
- 一次请求,可以在转发的资源间使用request共享数据。
使用request.getRequestDispatcher("/path").forward(request, response)进行请求转发:
1package com.linner.web;
2
3import javax.servlet.ServletException;
4import javax.servlet.annotation.WebServlet;
5import javax.servlet.http.HttpServlet;
6import javax.servlet.http.HttpServletRequest;
7import javax.servlet.http.HttpServletResponse;
8import java.io.IOException;
9
10@WebServlet("/demo1")
11public class RequestDemo1 extends HttpServlet {
12 @Override
13 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
14 System.out.println("demo1...");
15
16 // 存储数据
17 request.setAttribute("msg", "Hello");
18
19 // 请求转发(资源转发到demo2)
20 request.getRequestDispatcher("/demo2").forward(request, response);
21 }
22
23 @Override
24 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
25 this.doGet(request, response);
26 }
27}
1package com.linner.web;
2
3import javax.servlet.ServletException;
4import javax.servlet.annotation.WebServlet;
5import javax.servlet.http.HttpServlet;
6import javax.servlet.http.HttpServletRequest;
7import javax.servlet.http.HttpServletResponse;
8import java.io.IOException;
9
10@WebServlet("/demo2")
11public class RequestDemo2 extends HttpServlet {
12 @Override
13 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
14 System.out.println("demo2...");
15
16 // 获取数据
17 Object msg = request.getAttribute("msg");
18 System.out.println(msg);
19 }
20
21 @Override
22 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
23 this.doGet(request, response);
24 }
25}
通过http://localhost:8080/request-demo/demo1访问。
由于请求转发是在服务器内部的资源转发,所以
getRequestDispatcher()中的路径不需要包含虚拟目录。
请求转发使用HttpServletRequest(Request)对象进行资源的传递。这个用来存储资源的空间被称为Request域。
HttpServletRequest对象提供了对于Request中的域属性操作的方法有:
-
在 Request 域属性空间中放入数据:
1void setAttribute(String name, Object object)其生命周期与 Request 的生命周期相同。
-
从 Request 的域属性空间中获取指定名称的数据:
1Object getAttribute(String name) -
从 Request 的域属性空间中删除指定名称的数据:
1void removeAttribute(String name) -
创建请求转发器:
1RequestDispatcher getRequestDispatcher(String path)请求转发器中有一个方法,用于完成将请求对象转发给下一个资源:
1void forward(HttpServletRequest request, HttpServletResponse response)
Tomcat7 请求参数中文乱码问题
Tomcat8.0 之后,已经将默认编码设置为UTF-8。
POST请求参数是通过流的方式获取数据:
- Tomcat在获取流的时候采用的编码是
ISO-8859-1。 - 页面设置的编码格式一般为
UTF-8。 ISO-8859-1编码是不支持中文的,所以会出现乱码。
解决方案:通过HttpServletRequest提供的setCharacterEncoding(),在Tomcat在获取流数据之前的编码设置为UTF-8。
1package com.linner.web;
2
3import javax.servlet.ServletException;
4import javax.servlet.annotation.WebServlet;
5import javax.servlet.http.HttpServlet;
6import javax.servlet.http.HttpServletRequest;
7import javax.servlet.http.HttpServletResponse;
8import java.io.BufferedReader;
9import java.io.IOException;
10import java.net.URLDecoder;
11import java.nio.charset.StandardCharsets;
12
13@WebServlet("/test")
14public class RequestDemo4Copy extends HttpServlet {
15 @Override
16 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
17 }
18
19 @Override
20 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
21 // 1. 设置字符输入流的编码
22 request.setCharacterEncoding("UTF-8");
23
24 // 2. 获取请求参数
25 BufferedReader br = request.getReader();
26 String line = br.readLine();
27 // getReader()获取的是编码后的URL,如果要显示中文,需要对URL按照UTF-8进行解码
28 String decode = URLDecoder.decode(line, StandardCharsets.UTF_8);
29 System.out.println("解决乱码后: " + decode);
30 }
31}
GET请求参数包含在URL中:
getQueryString()获取的并不是字符输入流,所以setCharacterEncoding()并不适用。
-
浏览器在发送HTTP的过程中会根据页面
<meta>标签指定的charset的方式(一般为UTF-8)对URL进行编码。 URL编码:- 将字符串按照编码方式转为二进制。
- 每个字节(8位)转为2个16进制数(一个16进制数代表4位)并在前边加上
%。
-
Tomcat在接收编码后的URL后,会默认按照
ISO-8859-1进行URL解码。可以使用以下两个函数可以模拟URL编码、解码的过程:
-
编码:
1java.net.URLEncoder.encode(string, charset) -
解码:
1java.net.URLDecoder.decode(string, charset)
-
解决方案:
- 把字符数据(URL编码)按照ISO-8859-1编码转换成字节。
- 字节按照浏览器对应的URL编码(UTF-8)转换成对应的字符。
这样在转换的过程中保持编码一致,就可以解决中文乱码问题:
1package com.linner.web;
2
3import javax.servlet.ServletException;
4import javax.servlet.annotation.WebServlet;
5import javax.servlet.http.HttpServlet;
6import javax.servlet.http.HttpServletRequest;
7import javax.servlet.http.HttpServletResponse;
8import java.io.IOException;
9import java.net.URLDecoder;
10import java.nio.charset.StandardCharsets;
11
12@WebServlet("/test")
13public class RequestDemo4Copy extends HttpServlet {
14 @Override
15 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
16 String qs = request.getQueryString();
17 // 把字符数据(URL编码)按照ISO-8859-1编码转换成字节
18 byte[] bytes = qs.getBytes(StandardCharsets.ISO_8859_1);
19 // 按照UTF-8编码转换成对应的字符
20 String s = new String(bytes, StandardCharsets.UTF_8);
21 // 转换后的字符是URL编码后的字符,需要再次解码
22 qs = URLDecoder.decode(s, StandardCharsets.UTF_8);
23 System.out.println("解决乱码后: " + qs);
24 }
25
26 @Override
27 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
28 }
29}
通用方式解决乱码问题:
1package com.linner.web;
2
3import javax.servlet.ServletException;
4import javax.servlet.annotation.WebServlet;
5import javax.servlet.http.HttpServlet;
6import javax.servlet.http.HttpServletRequest;
7import javax.servlet.http.HttpServletResponse;
8import java.io.IOException;
9import java.nio.charset.StandardCharsets;
10
11@WebServlet("/test")
12public class RequestDemo4Copy extends HttpServlet {
13 @Override
14 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
15 // 1. 获取username
16 String username = request.getParameter("username");
17 // 2. 解决乱码
18 username = new String(username.getBytes(StandardCharsets.ISO_8859_1),
19 StandardCharsets.UTF_8);
20 System.out.println("解决乱码后: " + username);
21 }
22
23 @Override
24 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
25 this.doGet(request, response);
26 }
27}
HttpServletResponse
HTTP响应数据总共分为三部分内容,分别是:
-
响应行
如,
HTTP/1.1 200 OK,从左到右分别为:-
HTTP协议及版本
-
响应状态码 设置响应状态码,
HttpServletResponse对象提供了以下方法设置:1void setStatus(int sc) -
状态码描述
-
-
响应头
由多个
key: value组成。HttpServletResponse对象提供了以下方法设置键值对:1void setHeader(String name, String value) -
响应体
-
获取字符输出流:
1PrintWriter getWriter() -
获取字节输出流:
1ServletOutputStream getOutputStream()
-
Respones重定向
Response重定向(redirect)是一种资源跳转方式。如:
- 浏览器发送请求给服务器,服务器中对应的资源A接收到请求。
- 资源A现在无法处理该请求,就会给浏览器响应一个302的状态码和location(一个访问资源B的路径)。
- 浏览器接收到响应状态码为302就会重新发送请求到location对应的访问地址去访问资源B。
重定向的特点:
-
浏览器地址栏路径发送变化(由资源A的路径变化为资源B的路径)。
进行重定向访问时,由浏览器发送两次请求,所以地址发生了变化。
-
可以重定向到任意位置的资源(服务器内部、外部均可)。
资源由浏览器来访问,所以可以重定向到任意位置资源。
-
不能在多个资源使用Request重定向共享数据。
重定向是由浏览器来发送新的请求,每次请求中的Request对象都是不同的。
重定向需要两个步骤:
-
设置302状态码:
1response.setStatus(302) -
设置响应头中,
location的值:1response.setHeader("location", "/path_b")
Example:
1package com.linner.web;
2
3import javax.servlet.ServletException;
4import javax.servlet.annotation.WebServlet;
5import javax.servlet.http.HttpServlet;
6import javax.servlet.http.HttpServletRequest;
7import javax.servlet.http.HttpServletResponse;
8import java.io.IOException;
9
10@WebServlet("/demo1")
11public class ResponseDemo1 extends HttpServlet {
12 @Override
13 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
14 System.out.println("demo1...");
15
16 // 重定向
17 // 1. 设置相应状态码
18 resp.setStatus(302);
19 // 2. 设置相应头 Location (不区分大小写)
20 resp.setHeader("Location", "/request-demo/demo2");
21 }
22
23 @Override
24 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
25 this.doGet(req, resp);
26 }
27}
1package com.linner.web;
2
3import javax.servlet.ServletException;
4import javax.servlet.annotation.WebServlet;
5import javax.servlet.http.HttpServlet;
6import javax.servlet.http.HttpServletRequest;
7import javax.servlet.http.HttpServletResponse;
8import java.io.IOException;
9
10@WebServlet("/demo2")
11public class ResponseDemo2 extends HttpServlet {
12 @Override
13 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
14 System.out.println("demo2...");
15 }
16
17 @Override
18 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
19 this.doGet(req, resp);
20 }
21}
由于重定向是将重定向后的资源路径告知浏览器,所以
location需要添加虚拟目录(如/response-demo/demo2)。 如果是重定向到服务器外部资源,location的值为外部资源的URL。
HttpServletResponse提供了sendRedirect()方法来简化重定向流程。修改ResponseDemo1:
1package com.linner.web.response;
2
3import javax.servlet.ServletException;
4import javax.servlet.annotation.WebServlet;
5import javax.servlet.http.HttpServlet;
6import javax.servlet.http.HttpServletRequest;
7import javax.servlet.http.HttpServletResponse;
8import java.io.IOException;
9
10@WebServlet("/resp1")
11public class ResponseDemo1 extends HttpServlet {
12 @Override
13 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
14 System.out.println("resp1...");
15
16 // 简化方式完成重定向
17 // 动态获取虚拟目录
18 String contextPath = req.getContextPath();
19 resp.sendRedirect(contextPath + "/resp2");
20
21 @Override
22 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
23 this.doGet(req, resp);
24 }
25}
响应字符数据
将字符数据写回到浏览器,需要:
-
通过
HttpServletResponse对象获取字符输出流:1PrintWriter writer = response.getWriter() -
通过字符输出流写数据:
1writer.write("你好")
Example:
1package com.linner.web;
2
3import javax.servlet.ServletException;
4import javax.servlet.annotation.WebServlet;
5import javax.servlet.http.HttpServlet;
6import javax.servlet.http.HttpServletRequest;
7import javax.servlet.http.HttpServletResponse;
8import java.io.IOException;
9import java.io.PrintWriter;
10
11@WebServlet("/demo")
12public class ResponseDemo extends HttpServlet {
13 @Override
14 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
15 // 设置content-type(响应的数据格式)和字符集(编码)
16 resp.setContentType("text/html;charset=utf-8");
17 // content-type也可以使用setHeader()手动设置
18 // resp.setHeader("content-type", "text/html");
19
20 // 获取字符输出流
21 PrintWriter writer = resp.getWriter();
22 writer.write("你好");
23 writer.write("<h1>Hello World!</h1>");
24 //
25 }
26
27 @Override
28 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
29 this.doGet(req, resp);
30 }
31}
write()不仅能写入文本数据,还能写入HTML数据。PrintWriter对象会在HttpServletRequest被销毁时一并销毁,无需手动关闭。
响应字节数据
将字节数据写回到浏览器,需要:
-
通过
HttpServletResponse对象获取字节输出流:1ServletOutputStream os = response.getOutputStream() -
通过字节输出流写数据:
1os.write(buff)
Example:
1package com.linner.web;
2
3import javax.servlet.ServletException;
4import javax.servlet.ServletOutputStream;
5import javax.servlet.annotation.WebServlet;
6import javax.servlet.http.HttpServlet;
7import javax.servlet.http.HttpServletRequest;
8import javax.servlet.http.HttpServletResponse;
9import java.io.FileInputStream;
10import java.io.IOException;
11import java.io.PrintWriter;
12
13@WebServlet("/demo")
14public class ResponseDemo extends HttpServlet {
15 @Override
16 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
17 // 1. 读取文件
18 FileInputStream fis = new FileInputStream("src/main/webapp/imgs/bg.jpg");
19
20 // 2. 获取response字节输出流
21 ServletOutputStream os = resp.getOutputStream();
22
23 // 3. 完成流的copy
24 byte[] buff = new byte[1024];
25 int len = 0;
26 while ((len = fis.read(buff)) != -1) {
27 os.write(buff, 0, len);
28 }
29
30 fis.close();
31 }
32
33 @Override
34 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
35 this.doGet(req, resp);
36 }
37}
其中,流的copy可以使用IOUtils工具类的copy()来简化操作:
-
导入配置:
1<dependency> 2 <groupId>commons-io</groupId> 3 <artifactId>commons-io</artifactId> 4 <version>2.6</version> 5</dependency> -
修改
ResponseDemo:1@Override 2protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 3 // 1. 读取文件 4 FileInputStream fis = new FileInputStream("src/main/webapp/imgs/reg_bg_min.jpg");0 5 6 // 2. 获取response字节输出流 7 ServletOutputStream os = resp.getOutputStream(); 8 9 // 3. 完成流的copy 10 IOUtils.copy(fis, os); 11 12 fis.close(); 13}
总结
HttpServletRequest 常用方法
| 方法名 | 作用 |
|---|---|
String getMethod() |
获取请求方式 |
String getContextPath() |
获取虚拟目录(项目访问路径) |
StringBuffer getRequestURL() |
获取URL(统一资源定位符) |
String getRequestURI() |
获取URI(统一资源标识符) |
String getQueryString() |
获取请求参数(GET方式) |
String getHeader(String name) |
获取name指定key对应的请求头的value |
ServletInputStream getInputStream() |
获取请求体字节输入流(POST方式获取请求参数) |
BufferedReader getReader() |
获取请求体字符输入流getReader()获取请求参数后,还需要使用readLine()读取参数数据即, BufferedReader br = request.getReader();String line = br.readLine(); |
Map<String, String[]> getParameterMap() |
获取所有请求参数Map集合 |
String[] getParameterValues(String name) |
根据名称获取请求参数值 返回值为数组 返回多个参数 |
String getParameter(String name) |
根据名称获取请求参数值 返回单个参数值 |
void setAttribute(String name, Object object) |
在 Request 域属性空间中放入数据 |
Object getAttribute(String name) |
从 Request 的域属性空间中获取指定名称的数据 |
void removeAttribute(String name) |
从 Request 的域属性空间中删除指定名称的数据 |
RequestDispatcher getRequestDispatcher(String path) |
创建请求转发器 请求转发器中有一个方法,用于完成将请求对象转发给下一个资源: void forward(HttpServletRequest request, HttpServletResponse response) |
void setCharacterEncoding(String charset) |
设置请求体字符输入流的编码 |
HttpServletResponse 常用方法
| 方法名 | 作用 |
|---|---|
void setStatus(int sc) |
设置响应状态码 |
void setHeader(String name, String value) |
设置响应头键值对 |
PrintWriter getWriter() |
获取响应体字符输出流获取字符输出流后还需要使用write()方法来写入字符数据:PrintWriter writer = response.getWriter();writer.write("Hello World!"); |
ServletOutputStream getOutputStream() |
获取响应体字节输出流 获取字节输出流前需要先使用 FileInputStream对象来读取文件然后使用 IOUtils工具类的copy()来copy流获取字节输出流后还需要使用 write()方法来写入字符数据响应字节数据 |
void sendRedirect(String path) |
Respones重定向 |
评论