目录
- Servlet Response
- 1. Response 的体系结构
- 2 设置 HTTP 响应消息
- a. 响应行
- b. 响应头
- c. 响应体
- 3. 案例:响应重定向
- a. 主要需求
- b. 步骤分析
- c. 重定向特点
- d. 代码实现
- e. 效果展示
- 4. 请求转发与请求重定向的区别
- a. Request 的请求转发
- b. 请求转发与请求重定向的原理
- c. 请求转发与请求重定向的区别
- d. 使用场景
- 5. 案例:响应定时刷新
- a. 主要需求
- b. 步骤分析
- c. 代码实现
- 6. 案例:响应中文
- a. 主要需求
- b. 步骤分析
- c. 解决中文乱码
- d. 代码实现
- 7. 综合案例:点击切换验证码
- a. 主要需求
- b. 用 Java 制作一个验证码
- c. 前端页面
- 8. 综合案例:文件下载
- a. 文件目录
- b. 主要需求
- c. 方式一:直接使用超文本链接下载文件
- i. 前端页面
- ii. 效果
- iii. 缺点
- d. 方式二:使用 Servlet 下载文件
- i. 步骤分析
- ii. 前端页面
- iii. DownloadServlet
- iv. 解决文件名中文乱码问题
- v. 效果
Servlet Response
- Response 对象表示 web 服务器给浏览器返回的响应信息。
- 开发人员可以使用 Response 对象的方法,设置要返回给浏览器的响应信息。
1. Response 的体系结构
ServletResponse 接口
|
HttpServletResponse 接口
|
org.apache.catalina.connector.ResponseFacade 实现类(由 Tomcat 厂商提供的实现类)
2 设置 HTTP 响应消息
a. 响应行
- 格式:
协议/版本号 状态码
,例如HTTP/1.1 200
- 相关 API:
- 设置状态码:
void setStatus(int ss)
- 200 OK:请求已成功,请求所希望的响应头或数据体将随此响应返回。出现此状态码是表示正常状态。
- 302 Move temporarily:重定向,请求的资源临时从不同的 URI响应请求。
- 304 Not Modified:从缓存中读取数据,不从服务器重新获取数据。
- 403 Forbidden:服务器已经理解请求,但是拒绝执行它,一般在权限不够的时候常见。
- 404 Not Found:请求失败,请求所希望得到的资源未被在服务器上发现。
- 405 Method Not Allowed:请求行中指定的请求方法不能被用于请求相应的资源。
- 500 Internal Server Error:服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。
b. 响应头
- 格式:
响应头名称:响应头的值
,例如Location:
- 相关 API:
- 设置指定头名称和对应的值:
void setHeader(String name, String value)
c. 响应体
- 相关 API(输出流对象):
- 获取输出字符流:
PrintWriter getWriter()
- 获取输出字节流:
ServletOutputStream getOutputStream()
注意:
- 在同一个 Servlet 中,二种类型的输出流不能同时存在,互斥。
- 向浏览器输出文件时用字节流,输出文本内容时用字符流。
3. 案例:响应重定向
a. 主要需求
- 用户访问 AServlet 后,服务器告诉浏览器重定向到 BServlet。
b. 步骤分析
- 方式一:
- 设置状态码(响应行):
response.setStatus(302);
- 设置响应头 Location:
response.setHeader("Location","重定向网络地址");
- 方式二:
- Response 封装了专门处理重定向的方法:
response.sendRedirect("重定向网络地址");
c. 重定向特点
- 地址栏会发生改变;
- 重定向是二次请求;
- 重定向是客户端(浏览器)行为,可以跳转到服务器外部资源。
- 不能使用 Request 域共享数据
d. 代码实现
- AServlet:
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("/AServlet")
public class AServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*System.out.println("AServlet执行了....");
// 1.设置状态码
response.setStatus(302);
// 2.设置响应头
response.setHeader("Location","/webappPractice2/BServlet");*/
// 1.Response封装的专门处理重定向的方法
response.sendRedirect("/webappPractice2/BServlet");
}
}
- BServlet:
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("/BServlet")
public class BServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
e. 效果展示
- 访问 http://localhost:8080/webappPractice2/AServlet,页面输出:
4. 请求转发与请求重定向的区别
a. Request 的请求转发
- AServlet:
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("/AServlet")
public class AServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//往Request域中存储数据
request.setAttribute("name","Regino");
//请求转发到BServlet
request.getRequestDispatcher("/BServlet").forward(request,response);
}
}
- BServlet:
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("/BServlet")
public class BServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决向浏览器输出中文乱码问题
response.setContentType("text/html;charset=utf-8");
//从Request域中取出数据
String name = (String) request.getAttribute("name");
//向浏览器输出
response.getWriter().write("从Request域中获取到的数据:"+ name);
}
}
- 访问 http://localhost:8080/webappPractice2/AServlet,页面输出:
b. 请求转发与请求重定向的原理
- Request 转发请求原理:
- Tomcat 服务器创建 AServlet 的对象后,会创建一对 Request 与 Response 对象并用来调用 AServlet 的 doPost 方法。
- 当 Tomcat 服务器读到 AServlet 的转发请求时,会马上利用 AServlet 的那对 Request 与 Response 对象调用 BServlet 的 doPost 方法,并没有创建一对新的 Request 与 Response 对象。
- Response 请求重定向的原理:
- Tomcat 服务器创建 AServlet 的对象后,会创建一对 Request 与 Response 对象并用来调用 AServlet 的 doPost 方法。
- 当 Tomcat 服务器读到 AServlet 的重定向请求时,会马上把该请求返回给浏览器;浏览器发送重定向请求后,Tomcat 服务器读到浏览器的第二次请求时,会马上创建 BServlet 的对象,然后用新的一对 Request 与 Response 对象调用 BServlet 的 doPost 方法。
c. 请求转发与请求重定向的区别
- 请求转发的时候浏览器地址栏不变化的,请求重定向浏览器地址栏是变化的。
- 请求转发浏览器发出一次请求,请求重定向浏览器发出了两次请求。
- 请求转发的服务器只有一对请求和响应对象,而请求重定向的服务器有两对请求和响应对象。
- 请求转发发生在服务器,而请求重定向发生在浏览器。
- 地址不同,重定向需要加
项目根路径/
,而请求转发是模块内部的跳转,所以不用。 -
/WEB-INF
目录下的资源受保护,可以被请求转发访问到,但是重定向不行。
/
在进行资源跳转的时候,如果是给服务器去使用,那么/
代表了http://localhost:8080/项目根路径
;如果是给浏览器去使用的时候,那么/
代表了http://localhost:8080/
。
d. 使用场景
- 如果需要传递数据(Request域),使用转发;
- 如果不需要传递数据(Request域),使用重定向。
5. 案例:响应定时刷新
a. 主要需求
- 在当前页面停留 3 秒钟之后,跳转到个人博客首页。
b. 步骤分析
- 通过 Response 设置响应头 Refresh:
response.setHeader("Refresh","间隔时间(秒);跳转页面");
c. 代码实现
- RefreshServlet:
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("/refreshServlet")
public class RefreshServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 通过Response设置响应头Refresh
response.setHeader("Refresh", "3;");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("操作成功,3秒后跳转到Regino博客首页...");
}
}
6. 案例:响应中文
a. 主要需求
- 向页面输出中文数据没有乱码。
b. 步骤分析
- 通过 Response 获取字符输出流:
PrintWriter pw = response.getWriter();
- 通过字符输出输出文本:
pw.write("中文....");
c. 解决中文乱码
- 指定服务器响应编码方式:
response.setCharacterEncoding("GBK");
- 统一浏览器和服务器编码:
response.setContentType("text/html;charset=utf-8");
request.setCharacterEncoding("utf-8");
应该在获取请求参数之前设置,若已获取请求参数,此时还没有设置过编码,Tomcat 会设置默认 Post 请求参数编码为 ISO8859_1,那么再设置成 UTF-8 也无效了。所以应该用response.setContentType("text/html;charset=utf-8");
。
d. 代码实现
- EncodeServlet :
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("/encodeServlet")
public class EncodeServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 指定服务器响应编码方式
// response.setCharacterEncoding("UTF-8");//出现乱码
// 统一浏览器和服务器编码
response.setContentType("text/html;charset=utf-8");
// 1. 通过response获取字符输出流
PrintWriter pw = response.getWriter();
// 2. 通过字符输出输出文本
pw.write("中文....");
}
}
7. 综合案例:点击切换验证码
a. 主要需求
- 在页面展示登录验证码,点击此验证码可以更换新的验证码 。
- 作用:防止表单的恶意提交。
b. 用 Java 制作一个验证码
- 使用画图工具;
- 指定宽和高;
- 指定背景色;
- 生成四位随机数;
- 制作干扰线;
- 通过 Response 响应到浏览器。
- CheckcodeServlet:
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
@WebServlet("/CheckcodeServlet")
public class CheckcodeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 创建画布
int width = 120;
int height = 40;
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 获得画笔
Graphics g = bufferedImage.getGraphics();
// 填充背景颜色
g.setColor(Color.white);
g.fillRect(0, 0, width, height);
// 绘制边框
g.setColor(Color.red);
g.drawRect(0, 0, width - 1, height - 1);
// 生成随机字符
// 准备数据
String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
// 准备随机对象
Random r = new Random();
// 声明一个变量 保存验证码
String code = "";
// 书写4个随机字符
for (int i = 0; i < 4; i++) {
// 设置字体
g.setFont(new Font("宋体", Font.BOLD, 28));
// 设置随机颜色
g.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
String str = data.charAt(r.nextInt(data.length())) + "";
g.drawString(str, 10 + i * 28, 30);
// 将新的字符 保存到验证码中
code = code + str;
}
// 绘制干扰线
for (int i = 0; i < 6; i++) {
// 设置随机颜色
g.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g.drawLine(r.nextInt(width), r.nextInt(height), r.nextInt(width), r.nextInt(height));
}
// 将验证码 打印到控制台
System.out.println(code);
// 将验证码放到session中
request.getSession().setAttribute("code_session", code);
// 将画布显示在浏览器中
ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
c. 前端页面
- index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<img src="/webappPractice2/CheckcodeServlet" alt="Servlet随机验证码" id="img1"><br>
<script>
// 给图片绑定一个点击事件
document.getElementById('img1').onclick=function () {
// 重置src路径,重写发送请求
this.src='/webappPractice2/CheckcodeServlet?'+new Date().getTime();
// 后面加一个毫秒值的时间戳来欺骗浏览器,否则因为是同一个请求,验证码不刷新
}
</script>
</body>
</html>
- 访问 http://localhost:8080/webappPractice2/static/index.html,页面展示:
8. 综合案例:文件下载
a. 文件目录
b. 主要需求
- 用户点击页面的链接,浏览器开始下载文件。
c. 方式一:直接使用超文本链接下载文件
i. 前端页面
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Download</title>
</head>
<body>
<h3>文件下载</h3>
<h5>超文本链接下载</h5>
<a href="/webappPractice2/download/demo.doc">word文档</a><br>
<a href="/webappPractice2/download/test.zip">压缩包下载</a><br>
<a href="/webappPractice2/download/car.jpg">图片下载</a><br>
</body>
</html>
ii. 效果
- 访问 http://localhost:8080/webappPractice2/static/index.html 后,页面显示:
- 压缩包和word文档能直接下载,但是图片被直接被打开了。
iii. 缺点
- 如果要下载的是浏览器可解析的媒体类型,是直接打开而不是下载。
- 不能判断用户是否登录(VIP 权限问题),并进行限制。
d. 方式二:使用 Servlet 下载文件
i. 步骤分析
- 被下载文件的字节输入流:
FileInputStream
- Response 字节输出流:
ServletOutputStream
- 告知客户端下载文件的 MIME 类型(最新的浏览器此步骤可以省略):
Content-Type:MIME类型
- 告知浏览器以附件的方式保存:
Content-Disposition:attachment;filename=文件名
ii. 前端页面
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Download</title>
</head>
<body>
<h3>文件下载</h3>
<h5>超文本链接下载</h5>
<a href="/webappPractice2/download/demo.doc">word文档</a><br>
<a href="/webappPractice2/download/test.zip">压缩包下载</a><br>
<a href="/webappPractice2/download/car.jpg">图片下载</a><br>
<h5>Servlet下载</h5>
<a href="/webappPractice2/downloadServlet?filename=demo.doc">word文档</a><br>
<a href="/webappPractice2/downloadServlet?filename=test.zip">word文档</a><br>
<a href="/webappPractice2/downloadServlet?filename=car.jpg">图片下载</a><br>
</body>
</html>
iii. DownloadServlet
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.获取请求文件名
String filename = request.getParameter("filename");
// 2.获取文件真实路径,封装到字节输入流
ServletContext servletContext = request.getServletContext();
String realPath = servletContext.getRealPath("/download/" + filename);
FileInputStream in = new FileInputStream(realPath);
// 3.告诉浏览器MIME类型
String mimeType = servletContext.getMimeType(filename);
response.setContentType(mimeType);
// 4.告诉浏览器以附件方式保存
// 解决中文乱码和浏览器兼容性
String userAgent = request.getHeader("user-agent");
// 调用工具类处理
filename = DownLoadUtils.getName(userAgent, filename);
response.setHeader("content-disposition", "attachment;filename=" + filename);
// 5.获取字节输出流
ServletOutputStream out = response.getOutputStream();
// 6.IO流的拷贝
byte[] b = new byte[4096];// 4kb
int len = -1;
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
// 7.释放资源
out.close(); // out流对象,可以交给Tomcat关闭
in.close();
}
}
iv. 解决文件名中文乱码问题
- 如果该下载文件名是中文的话,会出现乱码。
- 需要考虑浏览器兼容性问题:
- 谷歌和绝大多数的浏览器是通过 URL编码,相关方法:
URLEncode()
编码URLDecode()
解码 - 火狐浏览器是 base64编码
- 判断浏览器不同编码的工具类 DownLoadUtils:
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import sun.misc.BASE64Encoder;
public class DownLoadUtils {
public static String getName(String agent, String filename) throws UnsupportedEncodingException {
if (agent.contains("Firefox")) {
// 火狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}
v. 效果
- 访问 http://localhost:8080/webappPractice2/static/index.html 后,页面显示:
- 图片、压缩包和word文档都能被直接下载。