- 第三章:请求与响应
- 一、HttpServletResponse对象
- 1.发送状态码相关的方法
- (1)setStatus(int status):
- (2)sendError(int sc):
- 2.发送响应消息头相关的方法
- (1)void setLocale(Locale loc):
- (2)void setCharacterEncoding(String charset):
- 3.发送响应消息体相关的方法
- (1)getOutputStream()
- (2)getWriter()
- 二、HttpServletResponse应用案例
- 应用1:解决中文输出乱码的问题
- (1)中文输出乱码的原因:
- (2)中文乱码问题的处理:
- 应用2:实现网页定时刷新并跳转
- (1)实现网页定时跳转:
- (2)实现网页定时刷新:
- 应用3:实现请求重定向
- (1)请求重定向问题:
- (2)请求重定向问题的实现:
- 三、HttpServletRequest对象
- 1.获取请求行消息的相关方法
- 2.获取请求消息头的相关方法
- 补充:利用Referer请求头防止盗链
- 四、HttpServletRequest应用案例
- 应用1:获取请求参数
- 应用2:解决请求参数的中文乱码问题
- (1)post提交方式的乱码问题处理:
- (2)get提交方式的乱码问题处理
- 应用3:通过Request对象传递数据
- 五、RequestDispatcher对象的应用
- 1.RequestDispatcher接口
- 2.`forward()`请求转发
- 3.`include()`请求包含
Servlet的主要作用是处理客户端请求,并作出一定的响应。如下图为浏览器访问Servlet的交互过程:
一、HttpServletResponse对象 1.发送状态码相关的方法注意:
- 在Web服务器运行阶段,每个Servlet都只会创建一个实例对象。
- 每次HTTP请求Web服务器都会调用所请求Servlet实例的service()方法,重新创建一个request对象和一个response对象。
本章主要针对request对象和response对象进行详细的讲解
当Servlet向客户端回送响应消息时,需要在响应消息中设置状态码。在HttpServletResponse接口中定义了两个发送状态码的方法:
(1)setStatus(int status):用于设置HTTP响应消息的状态码,并生成响应状态行。
由于响应状态行中的状态描述信息直接与状态码相关,而HTTP版本由服务器确定;因此只要通过setStatus(int status)方法设置了状态码,即可实现状态行的发送。
(2)sendError(int sc):注意:正常情况下,Web服务器会默认产生一个状态码为200的状态行
用于发送错误信息的状态码。
例如,404状态码表示找不到客户端请求的资源。在response对象中提供了两个重载的sendError(int sc)方法:
//method1:只用于发送错误信息状态码 public void sendError(int code) throws java.io.IOException //method2:只用于发送错误信息状态码,并增加一条提示说明的文本(将出现在客户端的正文内容当中) public void sendError(int code, String message) throws java.io.IOException2.发送响应消息头相关的方法
当Servlet向客户端发送响应消息时,由于HTTP协议的响应头字段有很多种,
为此在HttpServletResponse接口中,定义了一系列设置HTTP响应头字段的方法,如下图:
(1)void setLocale(Locale loc):- 对于HTTP协议,该方法就是设置Content-Language与Content-Type中的字符集编码部分
- 如果HTTP消息没有设置Content-Type头字段,则该方法设置的字符集编码不会出现在HTTP消息的响应头中
- 如果调用setCharacterEncoding()或setContentType()方法指定了响应内容的字符集编码,该方法设置的字符集编码将失效
- 对于HTTP协议,该方法就是设置Content-Type中的字符集编码部分
- 如果没有设置Content-Type头字段,该方法设置的字符集编码也不会出现在HTTP消息的响应头中
- 该方法设置的字符集编码优先权最高,可覆盖setContentType与setLocale方法中的字符集设置
3.发送响应消息体相关的方法注意:6、7、8种方法都是用于设置字符编码,可有效解决乱码问题
在HTTP响应消息中,大量数据都是通过响应消息体传递的;
ServletResponse遵循以IO流传递数据的设计原理,在发送响应消息体时,定义了两个与输出相关的方法:
(1)getOutputStream()由于ServletOutputStream类型的对象可以直接输出字节数组中的二进制数据,
因此需要输出二进制格式的响应正文,就需要使用getOutputStream()方法
(2)getWriter()由于PrintWriter类型的对象可以直接输出字符文本内容,
因此需要输出字符文本的网页文档,就需要使用getWriter()方法
二、HttpServletResponse应用案例 应用1:解决中文输出乱码的问题 (1)中文输出乱码的原因:特别注意的是:虽然response对象的getOutputStream()与getWriter()方法都可以发送响应消息体,但相互排除不可同时使用
由于计算机数据都是以二进制形式存储的,在传输文本时将不可避免de 发生字符与字节的转换问题。
补充:字符与字节的转换(字符与字节的转换是通过查码表完成的)
- 编码:将字符转换为字节的过程
- 解码:将字节转换为字符的过程
如果编码与解码使用的码表不一致,就会导致乱码问题的出现,因此需要需要考虑的方面如下:
point1:response对象的字符输出流在编码时,采用的字符码表(ISO-8859-1)
response对象默认采用的ISO-8859-1码表,并不兼容中文会将其编码为63(无法编码),
可以使用setCharacterEncoding()方法,对字符的编码方式进行设置。
response.setCharacterEncoding("utf-8");
point2:浏览器在进行解码时,采用的字符码表(GB2312)
浏览器默认采用的解码方式是GB2312,如果解码方式与编码方式不同将同样导致乱码问题。
可以使用setHeader()方法,对浏览器的解码方式进行设置。
response.setHeader("Content-Type", "text/html; charset = utf-8");
(2)中文乱码问题的处理:注意:
根据前文一>2中的介绍,具有同样设置编码、解码效果的还有response对象提供的setContentType()方法:
response.setContentType("text/html; charset = utf-8");该方法直接设置了编码、解码使用的码表,更加简洁的处理了中文乱码问题
package c4p1;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class ChineseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// response.setContentType("text/html;charset=utf-8");
String data = "HttpServletResponse应用:使用Content-Type方法处理中文输出乱码问题";
PrintWriter out = response.getWriter();
out.println(data);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
应用2:实现网页定时刷新并跳转注意:xml配置文件已省略
在web开发中,有时需要实现定时跳转页面的需求,
在HTTP协议中,定义了一个Refresh头字段可以指定浏览器在指定时间内自动刷新/跳转到其他页面。
(1)实现网页定时跳转:package c4p1;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class RefreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 两秒后刷新并跳转到csdn的首页
response.setHeader("Refresh", "2;URL=https://www.csdn.net/");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
(2)实现网页定时刷新:
package c4p1;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class RefreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("Refresh", "3"); //实现每3秒自动刷新
response.getWriter().println(new java.util.Date()); //输出当前时间
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
应用3:实现请求重定向
(1)请求重定向问题:
某些情况下,针对客户端的请求一个Servlet类可能无法完成全部工作,这时可以使用请求重定向(多个Servlet)完成。
在HttpServletResponse接口中定义了一个sendRedirect()方法,用于生成302响应码和Location响应头,
从而通知客户端重新访问Location响应头中的指定的URL,其完整语法如下:
public void sendRedirect(java.lang.String location) throws java.io.IOException
(2)请求重定向问题的实现:注意:参数location可以使用相对URL地址,Web服务器会自动将其翻译为绝对地址,再生成Location头字段
package c4p2;
import java.io.IOException;
import org.apache.catalina.connector.Response;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class LoginServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset:utf-8");
// 使用HttpServletRequest对象的getParameter()方法获取用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
// 实现请求重定向
if (("itcast").equals(username) && ("123").equals(password)) {
response.sendRedirect("/chapter04_reqandres/welcome.html");
} else {
response.sendRedirect("/chapter04_reqandres/login.html");
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
Insert title here
Insert title here
欢迎你!登录成功!
三、HttpServletRequest对象 1.获取请求行消息的相关方法注意:如果form表单post提交的路径更改为/chapter04_reqandres/WebContent/welcome.html将会找不到目标文件(有关文件路径问题)
当访问Servlet时,会在请求消息的请求行中包含请求方法、请求资源名、请求路径等信息。
为了获取这些信息,在HttpServletRequest接口中定义了一系列用于获取请求行的方法,具体使用如下所示:
package c4p3;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class RequestLineServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset = utf-8");
PrintWriter out = response.getWriter();
// 获取请求行的相关信息
out.println("getMethod:" + request.getMethod() + "
");
out.println("getRequestURI:" + request.getRequestURI() + "
");
out.println("getQueryString:" + request.getQueryString() + "
");
out.println("getProtocol:" + request.getProtocol() + "
");
out.println("getContextPath:" + request.getContextPath() + "
");
out.println("getPathInfo:" + request.getPathInfo() + "
");
out.println("getPathTranslated:" + request.getPathTranslated() + "
");
out.println("getServletPath:" + request.getServletPath() + "
");
out.println("getRemoteAddr:" + request.getRemoteAddr() + "
");
out.println("getRemoteHost:" + request.getRemoteHost() + "
");
out.println("getRemotePort:" + request.getRemotePort() + "
");
out.println("getLocalAddr:" + request.getLocalAddr() + "
");
out.println("getLocalName:" + request.getLocalName() + "
");
out.println("getLocalPort:" + request.getLocalPort() + "
");
out.println("getScheme:" + request.getScheme() + "
");
out.println("getRequestURI:" + request.getRequestURI() + "
");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
2.获取请求消息头的相关方法
当请求Servlet时,需要通过请求头向服务器传递附加信息,例如:客户端接收的数据类型、压缩方式、语言等。
在HttpServletRequest接口中,定义了一系列用于获取HTTP请求头字段的方法:
package c4p3;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class RequestHeadersServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset = utf-8");
PrintWriter out =response.getWriter();
// 获取请求消息中所有的头字段
Enumeration headerNames = request.getHeaderNames();
// 使用循环遍历所有的请求头,并通过getHeader()方法获取指定名称的头字段
while (headerNames.hasMoreElements()) {
String headerName = (String) headerNames.nextElement();
out.print(headerName + " : " + request.getHeader(headerName) + "
");
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
补充:利用Referer请求头防止盗链
在实际开发中经常使用Referer请求头字段,
利用Referer请求头字段可以实现,检查请求来源(只接受本站连接发送的下载请求,阻止其他站点链接的下载请求):
package c4p3;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class DownManagerServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset = utf-8");
PrintWriter out =response.getWriter();
// 获取Referer头的值
String referer = request.getHeader("referer");
// 获取访问地址
String sitePart = "http://" + request.getServerName();
// 判断referer头是否为空,这个头的首地址是否以sitePart开始的
if (referer != null && referer.startsWith(sitePart)) {
// 处理正在下载的请求
out.println("dealing download ...");
} else {
// 非法下载请求跳转到download.html页面
RequestDispatcher rDispatcher = request.getRequestDispatcher("/download.html");
rDispatcher.forward(request, response);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
结果分析1:在浏览器中输入地址直接访问DownManagerServlet(模拟其他站点链接的下载请求),由于第1次请求时请求消息中不含Referer请求头,所以DownManagerServlet将下载请求转发给了download.html页面
四、HttpServletRequest应用案例 应用1:获取请求参数结果分析2:点击页面跳转后的download链接,这时由于请求消息中包含了Referer请求头,并且其值与DownManagerServlet位于同一个站点,因此DownManagerServlet接受了下载请求
在实际开中,经常需要获取用户提交的表单数据,如:用户名、密码、电子邮箱等。
在ServeltRequest接口中,定义了一些获取请求参数的方法如下:
应用2:解决请求参数的中文乱码问题 (1)post提交方式的乱码问题处理:form表单通过post方式提交会把参数首先提交到request对象的缓冲区中,缓冲区的默认编码是ISO-8859-1(不支持中文)
处理方式:将request缓冲区的编码方式设置为支持中文的码表格式:例如使用setCharacterEncoding()方法
request.setCharacterEncoding("utf-8");
(2)get提交方式的乱码问题处理注意:setCharacterEncoding()只对POST提交方式的乱码问题有效,而对GET方式无效
对于get提交方式的乱码问题,使用String的构造方式来解决中文乱码问题:new String(bytes[], encodingMethod);
原理:先使用错误码表ISO-8859-1进行重新编码,然后使用码表utf-8进行解码。
String newvalue = new String(value.getBytes("ISO-8859-1"), "utf-8");
应用3:通过Request对象传递数据补充:对于GET请求参数的乱码问题,除了通过重新编码&解码,还可以通过配置Tomcat服务器来解决
Request对象不仅可以获取一系列数据,还可以通过属性传递数据。
在ServeltRequest接口中,定义了一些操作属性的方法,具体如下:
public void setAttribute(java.lang.String name, java.lang.Object o); public java.lang.Object getAttribute(java.lang.String name); public void removeAttribute(java.lang.String name); public java.util.Enumeration getAttributeNames();
五、RequestDispatcher对象的应用 1.RequestDispatcher接口注意:只有属于同一个请求中的数据才能够通过ServletRequest对象进行数据传递
除了使用sendRedirect()方法能够实现请求重定向外,还可以通过RequestDispatcher接口的实例对象来实现:
在ServletRequest接口中定义了一个获取RequestDispatcher对象的方法,如下:
RequestDispatcher getRequestDispatcher(String path)
2.forward()请求转发注意:根据RequestDispatcher对象提供方法功能,其中的forward方法可用于实现请求转发、include方法可用于实现请求包含
在Servlet中,如果当前Web资源不处理可以通过forward()方法将当前请求传递给其他的Web资源进行处理,称为请求转发。
package c4p5;
import java.io.IOException;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class RequestForwardServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset = utf-8");
// 将数据存储到request对象中
request.setAttribute("weather", "rainy");
RequestDispatcher dispatcher = request.getRequestDispatcher("/ResultServlet");
dispatcher.forward(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
package c4p5;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class ResultServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset=utf-8");
PrintWriter out =response.getWriter();
// 从request对象中获取保存的数据
String weather = (String) request.getAttribute("weather");
if (weather != null) {
out.println("今天的天气:" + weather + "
");
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
分析:地址栏中显示的是RequestForwardServlet的请求路径,页面显示了ResultServlet的输出内容
3.include()请求包含重点补充:请求转发与请求重定向的区别
- 请求重定向中有两次request请求、两次response响应,而请求转发有一次request请求、一次response响应
- 由于请求转发只有一次request请求,所以可以使用request属性进行数据共享
- 转发动作是在服务器内部实现的(转发路径不需要携带项目名称),重定向是在浏览器端执行的(路径需要项目名称)
请求包含是使用include()方法将Servlet请求转发给其他Web资源进行处理,
与请求转发不同的是响应返回结果,既包含了当前Servlet的响应消息、也包含了其他Web资源作出的响应消息:
package c4p6;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class IncludingServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置字符编、解码使用码表必须在IncludingServlet类中设置
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
RequestDispatcher dispatcher = request.getRequestDispatcher("/IncludedServlet?parameter=abcdefg");
out.println("before including" + "
");
dispatcher.include(request, response);
out.println("after including" + "
");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
package c4p6;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class IncludedServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("中国" + "
");
out.println("URL:" + request.getRequestURI() + "
");
out.println("QueryString:" + request.getQueryString() + "
");
out.println("Parameter:" + request.getParameter("parameter") + "
");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
注意:设置字符编、解码使用码表必须在IncludingServlet类中设置,IncludedServlet中设置的码表并不起作用。这是因为浏览器在请求IncludingServlet时,用于封装响应消息的HttpServletResponse对象已经创建(默认编码采用ISO-8859-1)。
更新日志:
2021/10/14:
1.新增`forward()请求转发部分的请求转发与请求重定向之间的区别
2.更新、简化forward与include方法的原理图示、新增included方法中使用setContentType方法无效的解释



