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