Appearance
Response
案例目标
- 制作图片下载 Servlet
- 统计某 Servlet 的访问次数
- 验证码生成
HTTP 响应
组成
响应行
texHTTP/1.1 200 OK
响应头
texServer: Apache/2.4.18(Ubuntu) Content-Encoding: gzip Content-Length: 4028 Connection: Keep-Alive Content-Type: text/html
响应体
html<!DOCTYPE html SYSTEM "about:legacy-compat"> <html lang="en"> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="res/css/tomcat.css" rel="stylesheet" type="text/css"> <link href="res/css/fonts/fonts.css" rel="stylesheet" type="text/css"> <title>Apache Tomcat® - Welcome!</title> <meta name="author" content="Apache Tomcat Project"> <meta name="google-site-verification" content="nA9F0GvGNjVCU9W7HBziONQVx6FehvgQglI1X7WPfRw"> </head> ...
响应行
tex
HTTP/1.1 200 OK
- 协议/版本
- 状态码及状态信息
常见的状态码
状态码 | 状态信息 | 说明 |
---|---|---|
200 | OK | 请求已成功,请求所希望的响应头或数据体将随此响应返回。出现此状态码是表示正常状态 |
302 | Move temporarily | 重定向,请求的资源临时从不同的 URI 响应请求 |
403 | Forbidden | 服务器已经理解请求,但是拒绝执行它 |
404 | Not Found | 请求资源不存在。通常是用户路径编写错误,也可能是服务器资源已删除 |
405 | Method Not Allowed | 请求行中指定的请求方法不能被用于请求相应的资源 |
500 | Internal Server Error | 服务器内部错误,通常程序抛异常 |
响应头
tex
Server: Apache/2.4.18(Ubuntu)
Content-Encoding: gzip
Content-Length: 4028
Connection: Keep-Alive
Content-Type: text/html
常见的响应头
状态头 | 说明 |
---|---|
Server | 描述服务器信息,包含Web容器信息、服务器系统信息 |
Content-Length | 描述响应体的长度(字符数) |
Content-Type | 请求体的编码格式,以及超文本类型。常见的有text/html, application/xml,application/json |
Location | 指定响应的路径,需要与状态码302配合使用,完成跳转Location: http://localhost:8080/another.. |
Refresh | 3;url=www.dubai.com。 |
Content-Disposition | attachment; filename=1.gif 常用于文件下载,attachment指定,被访问的资源是一个下载(attachment)的资源,下载所使用的文件名为(1.gif) |
响应体
html
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en">
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="res/css/tomcat.css" rel="stylesheet" type="text/css">
<link href="res/css/fonts/fonts.css" rel="stylesheet" type="text/css">
<title>Apache Tomcat® - Welcome!</title>
<meta name="author" content="Apache Tomcat Project">
<meta name="google-site-verification" content="nA9F0GvGNjVCU9W7HBziONQVx6FehvgQglI1X7WPfRw">
</head>
...
tex
服务器发送给浏览器的内容。可以是字符流,也可以是字节流。
Chrome查看响应信息
小结
HTTP 由哪两部分组成
响应由哪三部分组成
行
200 OK
404 Not Found
500 Internal Server Error
头
Location: 重定时,浏览器要跳转的地址
Refresh: 刷新当前页面
Content-Disposition: 告诉浏览器以附件形式下载,并且告诉浏览器,自动下载时的文件名
体
- 字符流
- 字节流
Response对象
HTTP 响应:包括行头体,服务端发给浏览器。请求内容放在 request 对象。服务器发给浏览器的信息放在 response 对象。
响应行相关API
void setStatus(int statusCode)
tex设置响应行-状态码,Tomcat不会协助添加错误信息
void sendError(int errorCode, String msg)
tex设置状态码(errorCode),并且Tomcat会协助添加错误信息(msg)
案例
- 通过设置状态码,观察浏览器信息
java
package com.futureweaver.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("/response_line")
public class ResponseLineServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置状态码(没有信息)
response.setStatus(500);
// 设置状态码(有信息,并且多一个"messgae")
response.sendError(500, "你吃了吗");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
响应头相关API
void setHeader(String name, String value)
tex在响应头中,设置一个Header。Header所对应的名字(name),以及Header所对应的值(value)
void setContentTypeString type
tex在响应头中,设置一个Header。Header所对应的名字为Content-Type,Header所对应的值为(value)
案例
- 过 3 秒跳转到另一个网站
java
package com.futureweaver.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;
// 过3秒跳到百度
@WebServlet("/refresh_sample")
public class RefreshSampleServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 过3秒跳到百度
response.setHeader("Refresh", "3;url=https://www.baidu.com");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
案例
- 利用 302 状态码,以及 Location 响应头,完成浏览器端页面跳转
- 浏览器两次请求
- 在浏览器完成的跳转
- 浏览器地址会变
java
package com.futureweaver.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("/redirect_sample")
public class RedirectSampleServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 手动重定向
// 设置状态码为302,Location为跳转的地址
response.setStatus(302);
response.setHeader("Location", request.getContextPath() + "/hello");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
浏览器跳转技术 - 重定向
- void sendRedirect(String location)
案例
- 利用 sendRedirect,完成浏览器端页面跳转
java
package com.futureweaver.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("/redirect_sample")
public class RedirectSampleServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// response.setStatus(302);
// response.setHeader("Location", request.getContextPath() + "/hello");
// 不需要手动设置302以及Location了
response.sendRedirect(request.getContextPath() + "/hello");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
重定向与转发的区别【重点】
- request.getRequestDispatcher("/资源地址").forward(request, response)
- response.sendRedirect("前端地址")
转发时,地址要有斜线;重定向时,地址没斜线
转发
- 一次请求
- 地址不变
- 服务器内部跳转
重定向
两次请求
地址会变
浏览器跳转
转发
javarequest.getRequestDispatcher("/bservlet").forward(request, response);
重定向
javaresponse.sendRedirect(request.getContextPath() + "/bservlet");
转发 | 重定向 |
---|---|
地址不变 | 地址改变 |
服务器内部跳转 | 浏览器完成跳转 |
一次请求 | 二次请求 |
响应体相关API【重点】
PrintWriter getWriter()
获取字符输出流(输出到浏览器)
OutputStream getOutputStream()
获取字节输出流(输出到浏览器,一般下载时或展示的是一个纯图片时使用)
案例
- 利用 getWriter,编写动态资源
java
package com.futureweaver.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("/hello")
public class HelloServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取字符输出流
PrintWriter writer = response.getWriter();
// 向字符输出流中,输出HTML内容
writer.println("<HTML>");
writer.println("<HEAD>");
writer.println("</HEAD>");
writer.println("<BODY>");
writer.println("<H1>Hello Servlet!</H1>");
writer.println("</BODY>");
writer.println("</HTML>");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
案例
- 利用 getWriter,编写中文动态资源
java
package com.futureweaver.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("/hello")
public class HelloServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取字符输出流
PrintWriter writer = response.getWriter();
// 可以选择write/print/println
// 向字符输出流中,输出HTML内容
writer.println("<HTML>");
writer.println("<HEAD>");
writer.println("</HEAD>");
writer.println("<BODY>");
writer.println("<H1>你好,Servlet!</H1>");
writer.println("</BODY>");
writer.println("</HTML>");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
响应体乱码解决
response.setCharacterEncoding("字符集")
tex响应体使用指定的字符集传输
response.setContentType("text/html; charset=utf-8")
texsetCharacterEncoding用于设置传输字符集,但是浏览器仍然不清楚页面所采用的字符编码。可以在Content-Type头给出。 如果在Content-Type头部指定了字符编码,则setCharacterEncoding可以省略。
java
package com.futureweaver.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("/hello")
public class HelloServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 在未设置字符编码之前,打印默认的字符编码
System.out.println(response.getCharacterEncoding());
// 服务器需要告诉浏览器我发送的响应体是什么字符编码
// 如果同时设置了Content-Type的编码,和response的characterENcoding的话,只需要写一个Content-Type即可
// response.setCharacterEncoding("utf-8");
// response.setHeader("Content-Type", "text/html; charset=utf-8");
response.setContentType("text/html; charset=utf-8");
PrintWriter writer = response.getWriter();
// 可以选择write/print/println
writer.println("<HTML>");
writer.println("<HEAD>");
writer.println("</HEAD>");
writer.println("<BODY>");
// 输出中文
writer.println("<H1>你好, Servlet!</H1>");
writer.println("</BODY>");
writer.println("</HTML>");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
案例
- 利用 getOutputStream,制作图片下载 Servlet
java
package com.futureweaver.web;
import org.apache.commons.io.IOUtils;
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.*;
@WebServlet("/download_sample")
public class DownloadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 告诉浏览器,这是一个附件,要下载,下载的文件名是download.gif
response.setHeader("Content-Disposition", "attachment; filename=download.gif");
// 获取浏览器字节输出流以及文件输入流
OutputStream out = response.getOutputStream();
InputStream in = new FileInputStream("/Users/LEAF/download.gif");
// 从输出流读取,写入到输出流
byte[] buf = new byte[1024];
int len = 0;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
// 关闭流对象
in.close();
out.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
【扩展】下载文件名为中文,乱码解决
因为在文件下载的过程中,服务器需要通过响应头部的Content-Disposition
,告诉浏览器下载的文件名。无论是请求,还是响应,行和头中的内容是不允许出现中文的。但是在下载的过程中,我们需要依赖响应头,设置一个中文的下载文件,那么就需要借助于 URLEncoder 来实现。
在百度中,输入中文"点墨星河",浏览器中能够看到地址栏中的中文,但是复制出来之后,发现浏览器中出现的每一个中文,都是以 3 个百分号为一组的一种编码形式,这种形式普遍在浏览器当中的 URL 出现。所以我们要使用的工具类,就是 URLEncoder
URLEncoder 有一个静态方法: encode(String character, String enc)
第一个参数是要编码的字符串
第二个参数是编码时采用的编码集
- ByteStreamServlet.java
java
package com.futureweaver.web;
import org.apache.commons.io.IOUtils;
import javax.servlet.ServletContext;
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.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
import java.net.URLEncoder;
// 字节流
@WebServlet("/byte_stream")
public class ByteStreamServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 中文使用URLEncoder.encode进行编码
response.setHeader("Content-Disposition", "attachment; filename="+ URLEncoder.encode("下载", "utf-8")+".gif");
// 使用字节输出流,制作图片下载
OutputStream out = response.getOutputStream();
// 调用ServletContext对象的getRealPath方法
ServletContext context = getServletContext();
// 获取项目当中的WEB-INF下面的文件的真实路径
InputStream in = context.getResourceAsStream("/WEB-INF/download.gif");
IOUtils.copy(in, out);
out.close();
in.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
响应体压缩
流程
- 构造方法
gzip = GZIPOutputStream(OutputStream out)
GZIPOutputStream会将写入到本对象当中的字符压缩,并且将压缩之后的内容,填入到out当中
- 向 GZIPOutputStream 填入数据
gzip.write(Byte[] byte)
- GZIPOutputStream 写入完成,并且压缩,把压缩好的内容填入到 out 当中
gzip.finish()
代码
java
package com.futureweaver.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.IOException;
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;
@WebServlet("/content_encoding")
public class ContentEncodingServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("Content-Encoding", "gzip");
// 获取字节流
OutputStream out = response.getOutputStream();
// Content-Encoding: gzip
GZIPOutputStream gzip = new GZIPOutputStream(out);
for (int idx = 0; idx < 1000; ++idx) {
gzip.write("abcd".getBytes());
}
gzip.finish();
out.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
IOUtils 工具包
- commons-io-2.1.jar
相关API
static int copy(InputStream input, OutputStream output)
tex从输入流当中,读取数据,输出到输出流当中 返回值: the number of bytes copied, or -1 if > Integer.MAX_VALUE 被拷贝了多少个字节,如果发生错误的是,返回是-1 如果超过了2GB的话,返回值是Inter.MAX_VALUE
案例
- 利用 IOUtils,改进图片下载 Servlet
实现
java
package com.futureweaver.web;
import org.apache.commons.io.IOUtils;
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.*;
@WebServlet("/download_sample")
public class DownloadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 告诉浏览器,这是一个附件,要下载,下载的文件名是download.gif
response.setHeader("Content-Disposition", "attachment; filename=download.gif");
// 获取浏览器字节输出流以及文件输入流
OutputStream out = response.getOutputStream();
InputStream in = new FileInputStream("/Users/LEAF/download.gif");
// IOUtils读取、写入
IOUtils.copy(in, out);
// 关闭流对象
in.close();
out.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
小结
响应行
响应头
response.setContentType("text/html; charset=utf-8") response.setHeader(name, value)
响应体
字符流 Writer out = response.getWriter()
字节流 OutputStream out = response.getOutputStream()
乱码
响应体乱码的解决 response.setContentType("text/html; charset=utf-8")
头部当中,如果出现了中文的话 URLEncoder.encode("中文", "utf-8")
IOUtils
commons-io.jar IOUtils.copy(in, out);
难点
- 转发
- 一次请求
- 地址不变
- 服务内部跳转
- 重定向
- 两次请求
- 地址会变
- 浏览器跳转
- 转发与重定向填写的地址区别
- 转发填写的地址,要有斜线
- 重定向填写的地址,不要有斜线
- 转发
学习目标总结
- 能够理解 HTTP 协议的响应部分
- 能够应用 Response 对象的方法
- 能够应用 Response 对象处理中文乱码
- 能够应用转发和重定向的方法
- 能够应用 Response 对象完成文件下载代码