Javaweb之 Request和Response详解
1. Request与Response
1.1. Web应用运行机制
到目前为止,我们已经掌握了Web应用程序的运行机制,现在学习的就是Web应用程序运行机制中很重要的内容 —— Request与Response。
首先,我们先来看一看Request与Response在Web应用程序运行时,是怎么样的。
通过上图的Web应用程序运行机制,我们可以知道关于Request与Response的信息:
- Web应用程序接收一次请求,就创建一个Request对象和Response对象。
- 通过Request对象可以在服务器端获取客户端发送的请求数据内容。
- 通过Response对象可以生成服务器端向客户端响应的数据内容。
- Request对象和Response对象并不是Web应用程序所创建的,而是由Tomcat服务器创建(JavaEE并没有Request与Response的实现类)。
- JavaEE提供了javax.servlet.http包中提供了HttpServletRequest和HttpServletResponse接口,这两个接口是继承于javax.servlet包中的ServletRequest和ServletResponse接口。
- javax.servlet包中的ServletRequest和ServletResponse接口是与协议无关的,而javax.servlet.http包中的HttpServletRequest和HttpServletResponse接口是与Http协议有关的。
1.2. 百度百科
- Request
Request这个对象不用事先声明,就可以在JSP网页中使用,在编译为Servlet之后,它会转换为javax.servlet.http.HttpServletRequest形态的对象,HttpServletRequest对象是有关于客户端所发出的请求的对象,只要是有关于客户端请求的信息,都可以藉由它来取得,例如请求标头、请求方法、请求参数、客户端IP,客户端浏览器等等信息。
- Response
Response对象用于动态响应客户端请示,控制发送给用户的信息,并将动态生成响应。Response对象只提供了一个数据集合cookie,它用于在客户端写入cookie值。若指定的cookie不存在,则创建它。若存在,则将自动进行更新。结果返回给客户端浏览器。
2. 掌握Response
2.1. Response概述
Response是Web应用程序用来封装向客户端响应信息的,是Servlet接口的service()方法的一个参数,类型为javax.servlet.http.HttpServletResponse。客户端每次发送请求时,服务器都会创建一个Response对象,并传递给Servlet接口的service()方法,来完成向客户端的响应工作。
下列是javax.servlet.http.HttpServletResponse常用的API列表:
Method Summary | |
---|---|
void | [addDateHeader](https://www.cnblogs.com/aaron911/p/7832055.html#addDateHeader(java.lang.String, long))(String name, long date) Adds a response header with the given name and date-value. |
void | [addHeader](https://www.cnblogs.com/aaron911/p/7832055.html#addHeader(java.lang.String, java.lang.String))(String name, String value) Adds a response header with the given name and value. |
void | sendRedirect(String location) Sends a temporary redirect response to the client using the specified redirect location URL. |
void | [setDateHeader](https://www.cnblogs.com/aaron911/p/7832055.html#setDateHeader(java.lang.String, long))(String name, long date) Sets a response header with the given name and date-value. |
void | [setHeader](https://www.cnblogs.com/aaron911/p/7832055.html#setHeader(java.lang.String, java.lang.String))(String name, String value) Sets a response header with the given name and value. |
void | setStatus(int sc) Sets the status code for this response. |
下列是javax.servlet.ServletResponse常用的API列表:
Method Summary | |
---|---|
ServletOutputStream | getOutputStream() Returns a ServletOutputStream suitable for writing binary data in the response. |
PrintWriter | getWriter() Returns a PrintWriter object that can send character text to the client. |
void | setCharacterEncoding(String charset) Sets the character encoding (MIME charset) of the response being sent to the client, for example, to UTF-8. |
void | setContentType(String type) Sets the content type of the response being sent to the client, if the response has not been committed yet. |
针对HttpServletResponse与ServletResponse常用的API,我们进行逐一讨论。
2.2. Response重定向
在学习Http响应协议时,我们曾做过重定向案例,但那时我们并不清楚其原理,下面我们就讨论一下利用HttpServletResponse来完成重定向的功能。
- 创建一个Servlet来完成重定向功能。
[](javascript:void(0)😉
public class ResponseServlet1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setStatus(302);
response.setHeader("Location", "/response/index.html");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
[](javascript:void(0)😉
- 在Web工程的web.xml文件中注册Servlet。
[](javascript:void(0)😉
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>ResponseServlet1</servlet-name>
<servlet-class>app.java.response.ResponseServlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ResponseServlet1</servlet-name>
<url-pattern>/response1</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
[](javascript:void(0)😉
- 创建一个HTML页面。
[](javascript:void(0)😉
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>index.html</title>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>Request与Response内容</h1>
</body>
</html>
[](javascript:void(0)😉
- 将Web工程发布到Tomcat服务器,并启动Tomcat服务器。
- 打开浏览器,在地址栏中输入http://localhost:8080/response/response1。
通过上述案例,我们可以发现在重定向中,实际上客户端向服务器端发送了两次请求,具体步骤如下:
- 在浏览器地址输入URL,访问服务器端的Servlet,这是第一次发送的请求。
- 服务器端接收到客户端的请求后,由Servlet做出处理,重定向到index.html页面,并响应给客户端浏览器。
- 客户端浏览器接收到服务器端的响应后,再次发送第二次请求。
- 服务器端接收到客户端的第二次请求后,并响应index.html给客户端浏览器显示。
其实Response对象提供了sendRedirect(String location)方法,同样可以完成重定向的工作。
[](javascript:void(0)😉
public class ResponseServlet1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.sendRedirect("/09_request&response/response/index.html");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
[](javascript:void(0)😉
2.3. 登录错误案例
利用Response对象提供了sendRedirect(String location)方法可以完成重定向的功能,实现登录功能中如果错误的案例。具体实现步骤如下:
- 创建一个用于登录的HTML页面。
[](javascript:void(0)😉
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>login.html</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>登录案例:登录错误重定向回当前页面</h1>
<form id="form1" action="/response/response2" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
[](javascript:void(0)😉
- 创建一个Servlet用于处理登录是否成功逻辑。
[](javascript:void(0)😉
public class ResponseServlet2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取客户端浏览器提交的用户名与密码内容
String username = request.getParameter("username");
String password = request.getParameter("password");
//模拟查询数据库:admin/admin
if(username.equals("admin")&&password.equals("admin")){
// 登录成功
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println("<h1>登录成功</h1>");
}else{
// 登录错误
response.sendRedirect("/response/response/login.html");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
[](javascript:void(0)😉
- 在Web工程的web.xml文件中注册Servlet。
[](javascript:void(0)😉
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>ResponseServlet2</servlet-name>
<servlet-class>app.java.response.ResponseServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ResponseServlet2</servlet-name>
<url-pattern>/response2</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
[](javascript:void(0)😉
- 将Web工程发布到Tomcat服务器,并启动Tomcat服务器。
- 打开浏览器,在地址栏中输入http://localhost:8080/response/response/login.html。
2.4. 自动刷新案例
在学习Http响应协议时,我们曾做过自动刷新案例,但那时我们并不清楚其原理,下面我们就讨论一下利用HttpServletResponse来完成自动刷新的功能。
- 创建一个Servlet用于设置5秒钟后自动刷新页面的功能(自动跳转到登录页面)。
[](javascript:void(0)😉
public class ResponseServlet3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("refresh", "5;url=/response/response/login.html");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println("当前页面会在5秒钟后自动跳转到登录页面.");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
[](javascript:void(0)😉
- 在Web工程的web.xml文件中注册Servlet。
[](javascript:void(0)😉
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>ResponseServlet3</servlet-name>
<servlet-class>app.java.response.ResponseServlet3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ResponseServlet3</servlet-name>
<url-pattern>/response3</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
[](javascript:void(0)😉
-
将Web工程发布到Tomcat服务器,并启动Tomcat服务器。
-
打开浏览器,在地址栏中输入http://localhost:8080/response/response3。
在学习HTML技术的时候,我们知道在head标签中有meta标签,该标签同样可以完成自动刷新页面的功能。
[](javascript:void(0)😉
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>refresh.html</title>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta http-equiv="refresh" content="5;url=/response/response/login.html">
</head>
<body>
<h1>当前页面会在5秒钟后自动跳转到登录页面.</h1>
</body>
</html>
[](javascript:void(0)😉
使用Response对象的setHeader()方法与HTML页面的标签,同样可以完成页面自动刷新功能,但是两者是有区别的:
- 使用Response对象的setHeader()方法:refresh信息是显示在响应头信息中。
- 使用HTML页面的标签:refresh信息是显示在响应体信息中。
在上述基础上,完成动态效果的倒计时功能,需要使用javascript技术来完成。
[](javascript:void(0)😉
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>refresh.html</title>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta http-equiv="refresh" content="5;url=/response/response/login.html">
</head>
<script type="text/javascript">
var times = 5;
function init(){
document.getElementById("times").innerHTML = times;
times--;
setTimeout("init()",1000);
}
</script>
<body onload="init();">
<h1>当前页面会在<span id="times"></span>秒钟后自动跳转到登录页面</h1>
</body>
</html>
[](javascript:void(0)😉
2.5. 禁止浏览器缓存案例
在学习Http响应协议时,我们知道响应协议中有三个头信息可以禁止浏览器本地缓存,分别是Cache-Control、Pragma和Expires。下面我们就通过一个案例来讨论一下。
- 创建一个Servlet用来向客户端浏览器响应一些数据内容。
[](javascript:void(0)😉
public class ResponseServlet4 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("当前时间是:"+new Date().toString());
System.out.println("已经成功地访问了当前Servlet...");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
[](javascript:void(0)😉
- 在Web工程的web.xml文件中注册Servlet。
[](javascript:void(0)😉
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>ResponseServlet4</servlet-name>
<servlet-class>app.java.response.ResponseServlet4</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ResponseServlet4</servlet-name>
<url-pattern>/response4</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
[](javascript:void(0)😉
-
打开浏览器,选择“工具”->“Internet选项”->“浏览历史记录”中的“设置”->“查看文件”,清空所有缓存内容。
-
在浏览器在地址栏中输入http://localhost:8080/response/response4进行访问。
-
这时再查看浏览器缓存内容的文件夹,会发现产生了这次访问的缓存内容。
-
这时再查看控制台信息,会发现打印了相关内容,说明客户端成功访问了服务器端的Servlet。
-
这时再次访问http://localhost:8080/response/response4地址,会发现内容不再变化,也不会产生请求和响应内容。
由于IE浏览器的本地缓存问题,第二次再次访问相同Servlet拦截路径时,不会再访问服务器端的Servlet,而是访问本地缓存内容。要想每次客户端访问都访问到服务器端的Servlet的话,我们需要禁止浏览器缓存机制。
- 我们可以通过响应头信息中的三个相关内容来设置禁止浏览器缓存。
[](javascript:void(0)😉
public class ResponseServlet4 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//设置响应头信息,禁止浏览器缓存.
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", -1);
PrintWriter out = response.getWriter();
out.println("<h1>已经成功地访问了当前Servlet...</h1>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
[](javascript:void(0)😉
-
重新发布Web工程到Tomcar服务器,并重启Tomcat服务器。
-
打开浏览器,清空本地缓存内容,在地址栏中输入相同URL地址。
-
但是,现在查看浏览器本地缓存文件夹,会发现没有任何缓存内容。
-
这时再次访问http://localhost:8080/response/response4地址。
-
而且控制台也会打印相关信息,说明第二次也成功地访问服务器端的Servlet。
2.6. 生成响应体内容
到目前为止,操作的都是Response对象的响应头信息。而Http协议的响应协议中,除了响应头信息之外,还有响应体,如何利用Response对象向客户端发送响应体呢?可以利用Response对象的getWriter()方法或getOutputStream()方法获取响应输出流,通过响应输出流向客户端进行响应。具体操作步骤如下:
- 创建一个Servlet用于向客户端响应内容。
[](javascript:void(0)😉
public class ResponseServlet5 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("<HTML>");
out.println(" <BODY>");
out.println(" <H1>Servlet生成的HTML页面.</H1>");
out.println(" </BODY>");
out.println("</HTML>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}}
[](javascript:void(0)😉
- 在Web工程的web.xml文件中注册Servlet。
[](javascript:void(0)😉
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>ResponseServlet5</servlet-name>
<servlet-class>app.java.response.ResponseServlet5</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ResponseServlet5</servlet-name>
<url-pattern>/response5</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
[](javascript:void(0)😉
-
打开浏览器,在地址栏中输入http://localhost:8080/response/response5。
-
发现在响应内容的页面中,中文显示为乱码。利用Response对象响应前,需要设置响应编码的字符集。
response.setCharacterEncoding("utf-8");
需要注意的是,调用Response对象的setCharacterEncoding()方法设置编码字符集时,必须要在调用Response对象的getWriter()方法或getOutputStream()方法之前。
-
重新打开IE浏览器,在地址栏中输入http://localhost:8080/response/response5。
-
但是,打开火狐浏览器,在地址栏中输入http://localhost:8080/response/response5。
虽然在响应之前设置了响应编码字符集,但是使用火狐浏览器显示时,依旧是乱码。
- 查看火狐浏览器的字符集编码,会发现并不是utf-8编码。
这说明利用Response对象的setCharacterEncoding()方法虽然可以设置响应体内容的编码字符集,但并不能通过浏览器,浏览器显示默认使用的编码集还是浏览器默认设置的。可以调用Response对象的setContentType()方法设置响应体的文件类型和编码字符集。
response.setContentType("text/html;charset=utf-8");
- 重新打开火狐浏览器,在地址栏中输入http://localhost:8080/response/response5乱码问题解决
需要注意的是,其实在调用Response对象的setContentType()方法时,设置的编码字符集是覆盖了Response对象的setCharacterEncoding()方法设置的编码字符集的。所以,实际上只调用setContentType()方法即可。
利用Response对象的getWriter()方法或getOutputStream()方法向客户端进行响应的时候,需要注意的问题是:
- 如果需要手动响应内容的时候,使用getWriter()方法,否则使用getOutputStream()方法。
- getWriter()方法和getOutputStream()方法之间是互斥的,也就是说只能使用其中一个方法。
- 使用getWriter()方法或getOutputStream()方法只能向客户端响应体,而不能修改响应行和响应头信息。
- getWriter()方法或getOutputStream()方法进行响应时,Tomcat服务器会自动关闭响应输出流,不必手动关闭响应输出流。
2.7. 生成验证码图片
利用Response对象向客户端进行响应的功能,来完成页面中验证码生成的案例,具体实现步骤如下:
- 创建一个HTML页面用于显示生成的验证码。
[](javascript:void(0)😉
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>identi.html</title>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<img src="/response/response6">
</body>
</html>
[](javascript:void(0)😉
- 创建一个Servlet用于生成页面实现的验证码。
[](javascript:void(0)😉
public class ResponseServlet6 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 当前Servlet的作用:生成HTML页面显示的验证码(图片).
* * Java生成图片内容,使用图形界面技术的awt、swing包.
*/
/*
* 1 在内存中创建图片
* * 创建图片,需要定义图片的宽度和高度.
* * 利用BufferedImage类来创建图片.
* * new BufferedImage(宽度, 高度, 图片类型)
*/
int width = 120;
int height = 30;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
/*
* 2 绘制图片背景颜色
* * 通过创建的图片对象的getGraphics()方法,获取画笔.
* * 通过画笔对象的setColor()方法设置图片的背景颜色.
* * 通过画笔对象的fillRect()方法设置背景颜色填充的面积.
*/
Graphics2D graphics2d = (Graphics2D)image.getGraphics();
graphics2d.setColor(Color.GRAY);
graphics2d.fillRect(0, 0, width, height);
/*
* 3 绘制边框
* * 通过画笔对象的drawRect()方法绘制边框的面积.
*/
graphics2d.setColor(Color.BLACK);
graphics2d.drawRect(1, 1, width - 1, height - 1);
/*
* 4 向图片中生成显示的验证码内容
* * 通过画笔对象的setFont()方法设置验证码内容的字体、大小等.
* * word表示生成验证码的备选文本内容.
*/
graphics2d.setColor(Color.RED);
graphics2d.setFont(new Font("新宋体", Font.BOLD, 24));
String word = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
// 这段代码用于将生成的验证码内容写入到图片中.
Random random = new Random();
int x = 5;
for (int i = 0; i < 4; i++) {
// 加入字体旋转 角度为"-30-30"之间.
int jiaodu = random.nextInt(60) - 30;
// 转换角度为弧度.
double theta = jiaodu * Math.PI / 180;
// 生成下标
int randomIndex = random.nextInt(word.length());
// 获取用于验证码显示的字符.
char c = word.charAt(randomIndex);
// 将字符写入图片.
graphics2d.rotate(theta, x, 20);
graphics2d.drawString(c + "", x, 20);
graphics2d.rotate(-theta, x, 20);
// 设置下一个字符出现的水平坐标.
x += 30;
}
// 5 绘制干扰线
graphics2d.setColor(Color.LIGHT_GRAY);
for (int i = 0; i < 10; i++) {
int x1 = random.nextInt(width);
int x2 = random.nextInt(width);
int y1 = random.nextInt(height);
int y2 = random.nextInt(height);
graphics2d.drawLine(x1, y1, x2, y2);
}
// 6 释放内存中的资源
graphics2d.dispose();
// 7 将生成的图片,响应到客户端浏览器
ImageIO.write(image, "jpg", response.getOutputStream());
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
[](javascript:void(0)😉
- 在Web工程的web.xml文件中注册Servlet。
[](javascript:void(0)😉
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>ResponseServlet6</servlet-name>
<servlet-class>app.java.response.ResponseServlet6</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ResponseServlet6</servlet-name>
<url-pattern>/response6</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
[](javascript:void(0)😉
- 打开浏览器,在地址栏中输入http://localhost:8080/response/response6。
如果验证码的内容设置为中文的话,只需要将上述代码中的word变量的值设置为常用汉字即可。
到目前为止,生成的验证码需要每次刷新页面才能重新生成验证码,如何实现鼠标点击验证码图片改变验证码内容呢?具体实现步骤如下:
- 需要在显示验证码图片的HTML页面中,使用JavaScript代码来实现。
[](javascript:void(0)😉
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>identi.html</title>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<script type="text/javascript">
function change(){
document.getElementById("myimg").src = "/response/response6";
}
</script>
<body>
<img src="/response/response6" id="myimg" onclick="change();" style="cursor: pointer;">
</body>
</html>
[](javascript:void(0)😉
这样实现之后,实际测试发现验证码内容并没有改变。原因是IE浏览器缓存问题,有两种方式来解决:
- 利用之前掌握的通过设置响应头信息中的内容,禁止浏览器保存缓存内容。
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", -1);
- 通过设置img图片每次请求的URL地址不同解决这个问题。
document.getElementById("myimg").src = "/response/response6?"+new Date().getTime();
3. 掌握Request
3.1. Request概述
Request是Web应用程序用来封装向客户端请求信息的,是Servlet接口的service()方法的一个参数,类型为javax.servlet.http.HttpServletRequest。客户端每次发送请求时,服务器都会创建一个Request对象,并传递给Servlet接口的service()方法,来完成向客户端的请求工作。
下列是javax.servlet.http. HttpServletRequest常用的API列表:
Method Summary | |
---|---|
String | getContextPath() Returns the portion of the request URI that indicates the context of the request. |
long | getDateHeader(String name) Returns the value of the specified request header as a long value that represents a Date object. |
String | getHeader(String name) Returns the value of the specified request header as a String. |
Enumeration | getHeaderNames() Returns an enumeration of all the header names this request contains. |
Enumeration | getHeaders(String name) Returns all the values of the specified request header as an Enumeration of String objects. |
int | getIntHeader(String name) Returns the value of the specified request header as an int. |
String | getMethod() Returns the name of the HTTP method with which this request was made, for example, GET, POST, or PUT. |
String | getPathInfo() Returns any extra path information associated with the URL the client sent when it made this request. |
String | getQueryString() Returns the query string that is contained in the request URL after the path. |
String | getRequestURI() Returns the part of this request’s URL from the protocol name up to the query string in the first line of the HTTP request. |
StringBuffer | getRequestURL() Reconstructs the URL the client used to make the request. |
String | getServletPath() Returns the part of this request’s URL that calls the servlet. |
下列是javax.servlet.ServletRequest常用的API列表:
Method Summary | |
---|---|
String | getCharacterEncoding() Returns the name of the character encoding used in the body of this request. |
String | getContentType() Returns the MIME type of the body of the request, or null if the type is not known. |
String | getParameter(String name) Returns the value of a request parameter as a String, or null if the parameter does not exist. |
String | getProtocol() Returns the name and version of the protocol the request uses in the form protocol/majorVersion.minorVersion, for example, HTTP/1.1. |
String | getRealPath(String path) Deprecated. As of Version 2.1 of the Java Servlet API, use ServletContext.getRealPath(java.lang.String) instead. |
String | getRemoteAddr() Returns the Internet Protocol (IP) address of the client or last proxy that sent the request. |
String | getRemoteHost() Returns the fully qualified name of the client or the last proxy that sent the request. |
int | getRemotePort() Returns the Internet Protocol (IP) source port of the client or last proxy that sent the request. |
RequestDispatcher | getRequestDispatcher(String path) Returns a RequestDispatcher object that acts as a wrapper for the resource located at the given path. |
String | getServerName() Returns the host name of the server to which the request was sent. |
int | getServerPort() Returns the port number to which the request was sent. |
3.2. 获取请求行信息
在学习Http请求协议时,知道Http请求协议包含请求行、请求头及请求体三个部分。首先我们来讨论请求协议中的请求行,请求行包含请求方式、请求链接及GET方式的参数和Http请求协议版本。
GET方式的请求行内容:
GET /http/01_get.html?username=zhangsan HTTP/1.1
POST方式的请求行内容
POST /http/02_post.html HTTP/1.1
获取Http请求协议中请求行的内容,可以通过以下方法:
- getMethod()方法:获取Http请求协议的请求方式,例如GET或POST等。
- getRequestRUI()方法:获取Http请求协议的资源路径。
- getProtocol()方法:获取Http请求协议的协议版本。
- getQueryString()方法:获取Http请求协议GET方式的请求参数。
下面我们通过代码实现,来验证一下上述方法的功能:
- 创建一个Servlet用于打印上述方法。
[](javascript:void(0)😉
public class RequestServlet1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取请求方式,GET或POST
System.out.println("请求方式:"+request.getMethod());
// 获取请求协议
System.out.println("请求协议:"+request.getProtocol());
// 获取请求路径URI和URL
System.out.println("请求路径URI:"+request.getRequestURI());
System.out.println("请求路径URL:"+request.getRequestURL());
// 获取请求参数
System.out.println("请求参数:"+request.getQueryString());
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
[](javascript:void(0)😉
- 在Web工程的web.xml文件中注册Servlet。
[](javascript:void(0)😉
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>RequestServlet1</servlet-name>
<servlet-class>app.java.request.RequestServlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RequestServlet1</servlet-name>
<url-pattern>/request1</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
[](javascript:void(0)😉
-
将Web工程发布到Tomcat服务器,并启动Tomcat服务器。
-
打开浏览器,在地址栏中输入http://localhost:8080/response/request1。
-
在地址后增加参数http://localhost:8080/response/request1?username=zhangsan。
除上述方法可以获取请求协议的请求行中信息外,我们还可以通过getRemoteAddr()方法获取客户端IP地址和getContextPath()方法获取Web工程虚拟目录名称。
// 获取客户端IP地址
System.out.println("客户端IP地址:"+request.getRemoteAddr());
// 获取Web工程虚拟目录名称
System.out.println("Web工程虚拟目录名称:"+request.getContextPath());
3.3. 获取请求头信息
下面我们来讨论Http请求协议中的请求头信息,下面是一个Http请求协议的请求头信息内容:
[](javascript:void(0)😉
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, */*
Referer: http://localhost:8080/07_http/01_get.html
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; qdesk 2.5.1277.202; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3)
Accept-Encoding: gzip, deflate
If-Modified-Since: Thu, 11 Sep 2014 02:44:35 GMT
If-None-Match: W/"679-1410403475587"
Host: localhost:8080
Connection: Keep-Alive
[](javascript:void(0)😉
我们之前曾利用请求头信息中的Referer完成了防盗链案例,下面我们来回顾一下。
- 首先我们创建一个网站的主页面。
[](javascript:void(0)😉
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>index.html</title>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<a href="refererServlet">特价商品</a>
</body>
</html>
[](javascript:void(0)😉
- 然后我们完成服务器端Servlet防止盗链的逻辑内容。
[](javascript:void(0)😉
public class RefererServlet extends HttpServlet {
// 处理GET方式的请求
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 判断请求中referer是否存在,有效 --- 防止盗链
String referer = request.getHeader("referer");
if(referer!=null && referer.equals("http://localhost:8080/http/index.html")){
// 有效
response.setContentType("text/html;charset=utf-8");
response.getWriter().println("笔记本1000元");
}else{
// 无效
response.setContentType("text/html;charset=utf-8");
response.getWriter().println("盗链真无耻!");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
[](javascript:void(0)😉
-
将工程发布到Tomcat服务器,启动Tomcat服务器。
-
打开浏览器,输入http://localhost:8080/http/index.html地址。
-
在网站主页面中,点击“特价商品”链接,会返回正确内容。
-
如果在浏览器地址栏中直接输入http://localhost:8080/http/refererServlet链接。
-
由于直接输入链接地址的请求协议中,没有referer信息,所以会显示报错信息。
在请求头信息中,除了Referer可以实现防盗链,还可以利用User-Agent获取客户端浏览器相关信息。
- 创建一个Servlet用于打印User-Agent请求头信息。
[](javascript:void(0)😉
public class RequestServlet2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("浏览器是:"+request.getHeader("user-agent"));
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
[](javascript:void(0)😉
- 在Web工程的web.xml文件中注册Servlet。
[](javascript:void(0)😉
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>RequestServlet2</servlet-name>
<servlet-class>app.java.request.RequestServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RequestServlet2</servlet-name>
<url-pattern>/request2</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
[](javascript:void(0)😉
- 将Web工程发布到Tomcat服务器,并启动Tomcat服务器。
- 打开浏览器,在地址栏中输入http://localhost:8080/response/request2。
我们还可以利用getHeaderNames()方法打印所有请求头信息。
Enumeration<String> names = request.getHeaderNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
System.out.println(name+":"+request.getHeader(name));
}
3.4. 获取请求参数
由于Http请求协议中的请求方式常用的有两种GET和POST,GET方式的请求参数在请求行中的资源路径后面,POST方式的请求参数在请求体中。
在服务器端Servlet获取请求参数共有以下四种方法:
- getParameter()方法:获取指定参数名的参数值(单个值)。
- getParameterValues()方法:获取指定参数名的参数值(多个值)。
- getParameterNames()方法:获取所有参数的参数名。
- getParameterMap()方法:获取参数以name=value形式存储在Map集合中,将Map集合返回。
首先,我们来讨论POST方式的请求参数,在服务器端如何获取:
- 创建一个HTML页面编写form表单用于提交客户端数据。
[](javascript:void(0)😉
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>POST方式请求.html</title>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>最复杂form表单</h1>
<form method="post" action="/response/request3">
用户名 <input type="text" name="username" /><br/>
密码 <input type="password" name="password" /><br/>
性别 <input type="radio" name="gender" value="男" /> 男 <input type="radio" name="gender" value="女" />女 <br/>
爱好 <input type="checkbox" name="hobby" value="体育" />体育
<input type="checkbox" name="hobby" value="音乐" />音乐
<input type="checkbox" name="hobby" value="读书" />读书<br/>
城市 <select name="city">
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
</select> <br/>
个人简介 <textarea rows="5" cols="60" name="introduce"></textarea><br/>
<input type="submit" value="提交" />
</form>
</body>
</html>
[](javascript:void(0)😉
- 创建一个Servlet用于接收客户端浏览器请求参数。
[](javascript:void(0)😉
public class RequestServlet3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
String gender = request.getParameter("gender");
String hobby = request.getParameter("hobby");
String city = request.getParameter("city");
String introduce = request.getParameter("introduce");
System.out.println("姓名:" + username);
System.out.println("密码:" + password);
System.out.println("性别:" + gender);
System.out.println("爱好:" + hobby);
System.out.println("城市:" + city);
System.out.println("个人介绍:" + introduce);
}
}
[](javascript:void(0)😉
- 在Web工程的web.xml文件中注册Servlet。
[](javascript:void(0)😉
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>RequestServlet3</servlet-name>
<servlet-class>app.java.request.RequestServlet3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RequestServlet3</servlet-name>
<url-pattern>/request3</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
[](javascript:void(0)😉
-
将Web工程发布到Tomcat服务器,并启动Tomcat服务器。
-
打开浏览器,在地址栏中输入http://localhost:8080/response/request/ POST.html。
-
在HTML页面中,输入相关信息后,点击“提交”按钮。
-
在控制台打印信息时,发现都是乱码。POST方式解决乱码,需要使用setCharacterEncoding()方法。
request.setCharacterEncoding("utf-8");
- 在查看控制台打印信息,这时的信息已经显示正常中文了。
获取页面请求的数据信息中,“爱好”内容为多选框,但实际上只打印其中一项。需要调用getParameterValues()方法来解决。
String[] hobby = request.getParameterValues("hobby");
System.out.println("爱好:" + Arrays.toString(hobby));
下面,我们来讨论一下GET方式的请求参数,在服务器端如何获取:
- 将HTML页面中表单的Method改为“GET”。
- 编写Servlet用于接收客户端浏览器请求参数。
[](javascript:void(0)😉
public class RequestServlet3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
String gender = request.getParameter("gender");
String hobby = request.getParameter("hobby");
String city = request.getParameter("city");
String introduce = request.getParameter("introduce");
System.out.println("姓名:" + username);
System.out.println("密码:" + password);
System.out.println("性别:" + gender);
System.out.println("爱好:" + hobby);
System.out.println("城市:" + city);
System.out.println("个人介绍:" + introduce);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
[](javascript:void(0)😉
-
打开浏览器,在地址栏中输入http://localhost:8080/response/request/ POST.html。
-
在控制台打印信息时,发现都是乱码。
POST方式解决乱码,有两种方式:
- 修改Tomcat服务器安装目录的conf目录中的server.xml文件。
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
RUIEncoding="utf-8" />
- 通过代码进行逆向解码(以用户名为例)。
username = URLEncoder.encode(username, "ISO-8859-1");
username = URLDecoder.decode(username, "utf-8");
或者也可以利用下面这种方式来解决逆向解码。
gender = new String(gender.getBytes("ISO-8859-1"), "utf-8");
当然,获取到客户端浏览器发送的请求参数内容后,还可以完成非空的验证功能。
if (username != null && username.length() > 0) {
System.out.println("username 有效.");
}
3.5. 请求转发数据
之前我们完成了重定向的功能,现在要完成请求转发的功能。这两个功能经常会放在一起比较:
- 重定向:
- 发生两次请求,两次响应。
- 重定向在客户端浏览器可以查看到。
- 重定向无法携带数据。
- 重定向中的第二次请求资源路径来源于客户端。
- 请求转发:
- 发生一次请求,一次响应。
- 请求转发在客户端浏览器无法查看。
- 请求转发可以携带数据。
- 请求转发的第二次请求资源路径来源于服务器端内部。
下面我们来实现请求转发的功能,再比较请求转发与重定向的区别。
- 创建一个Servlet用于接收客户端请求。
[](javascript:void(0)😉
public class RequestServlet4 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("这是Servlet4...");
// 获取请求转发对象
RequestDispatcher dispatcher = request.getRequestDispatcher("/request5");
// 利用forward()方法进行请求转发.
dispatcher.forward(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
[](javascript:void(0)😉
- 再创建一个Servlet用于接收请求转发的请求。
[](javascript:void(0)😉
public class RequestServlet5 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("这是Servlet5...");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
[](javascript:void(0)😉
- 在Web工程的web.xml文件中注册Servlet。
[](javascript:void(0)😉
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>RequestServlet4</servlet-name>
<servlet-class>app.java.request.RequestServlet4</servlet-class>
</servlet>
<servlet>
<servlet-name>RequestServlet5</servlet-name>
<servlet-class>app.java.request.RequestServlet5</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RequestServlet4</servlet-name>
<url-pattern>/request4</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RequestServlet5</servlet-name>
<url-pattern>/request5</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
[](javascript:void(0)😉
-
打开浏览器,在地址栏输入http://localhost:8080/response/request4。
-
通过Request对象的setAttribute()方法和getAttribute()方法来携带数据。
RequestServlet4
// 向Request对象中,存储数据内容.
request.setAttribute("name", "longestory");
RequestServlet5
String name = (String)request.getAttribute("name");
System.out.println("获取的name为:"+name);
- 打开浏览器,在地址栏输入http://localhost:8080/response/request4。
e"
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=“http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd”>
RequestServlet4
app.java.request.RequestServlet4
RequestServlet5
app.java.request.RequestServlet5
RequestServlet4
/request4
RequestServlet5
/request5
index.jsp
[[外链图片转存中...(img-aokrBWC9-1644508988205)]](javascript:void(0);)
- 打开浏览器,在地址栏输入http://localhost:8080/response/request4。
- 通过Request对象的setAttribute()方法和getAttribute()方法来携带数据。
RequestServlet4
// 向Request对象中,存储数据内容.
request.setAttribute(“name”, “longestory”);
RequestServlet5
String name = (String)request.getAttribute(“name”);
System.out.println(“获取的name为:”+name);
- 打开浏览器,在地址栏输入http://localhost:8080/response/request4。
- 而从客户端浏览器通过HttpWatch工具查看请求时,只发生了一次请求。