比较重要,懂得都懂
第一章 HTTP响应消息
1.1 简述
之前我们说过Http协议有两个消息
- 请求消息: 客户端发送给服务器端的数据
- 数据格式:
- 请求行
- 请求头
- 请求空行
- 请求体
- 数据格式:
- 响应消息: 服务器端发送给客户端的数据
- 数据格式:
- 响应行
- 响应头
- 响应空行
- 响应体
- 数据格式:
今天,我们要讨论的就是响应消息
我们看看响应行和响应头长什么样子
HTTP/1.1 200 OK //这一行是响应行,下面的全部都是响应头
Bdpagetype: 1
Bdqid: 0x853ce00900041412
Cache-Control: private
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Wed, 09 Jun 2021 09:58:10 GMT
Expires: Wed, 09 Jun 2021 09:57:18 GMT
Server: BWS/1.1
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=34042_33848_33855_33607_26350_22160; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Traceid: 162323269009712307309600794834906649618
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked
响应体节选(我们可以看到,其实响应体就是我们看到的网页)
<script data-compress=strip>
function h(obj){
obj.style.behavior='url(#default#homepage)';
var a = obj.setHomePage('//www.baidu.com/');
}
</script>
<script>
_manCard = {
asynJs : [],
asynLoad : function(id){
_manCard.asynJs.push(id);
}
};
window._sp_async = 1;
</script>
1.2 响应行
组成
- 协议/版本 响应状态码 状态码描述
响应状态码
状态码都是3位数字
分类:
- 1xx: 服务器接受客户端信息,但是只接收到了部分数据,等待一定时间后,就返回这个状态码,询问客户端还要不要发消息 (比较少见)
- 2xx: 响应成功 (常见)
- 3xx: 重定向, 属于资源跳转的一种方式 。常见的代表状态码如下
- 302(重定向:告诉客户端去访问另外一个资源或者页面)
- 304(访问缓存,假如客户端缓存里有了要请求的数据,那么服务器就返回这个告诉客户端访问自己的缓存,减轻服务器压力)
- 4xx: 客户端错误
- **404: ** 请求的路径没有对应的资源
- 405: 客户端的请求方式 在Servlet中没有对应的方法(doXXX)
- 5xx: 服务器端错误
- 500: 服务器端内部错误
除了这些示例,其他还有,遇到了网上查就行了
1.3 响应头
组成
若干个
头名称: 值
常见的响应头
- Content-Type: 服务器告诉客户端本次响应体的数据格式以及编码格式
Content-Type = "text/html;charset=utf-8"
- Content-disposition : 服务器告诉客户端以什么格式打开响应体数据
- 默认值:
inline
, 在当前页面打开 - 其他的:
attachment;filename="xxx"
, 以附件形式打开响应体 (文件下载的时候使用)
- 默认值:
- 有待补充
第二章 response对象
2.1 功能介绍
实现接口(HttpServletResponse)
功能: 设置响应消息
-
设置响应行
-
设置状态码:
void setStatus(int sc) Sets the status code for this response.
-
-
设置响应头
void setHeader(String name, String value) Sets a response header with the given name and value.
-
设置响应体(通过流的方式来获取,步骤如下)
-
获取输出流
//获取字节输出流 ServletOutputStream getOutputStream() throws IOException Returns a ServletOutputStream suitable for writing binary data in the response. The servlet container does not encode the binary data. //获取字符输出流 PrintWriter getWriter() throws IOException Returns a PrintWriter object that can send character text to the client. The PrintWriter uses the character encoding returned by getCharacterEncoding(). If the response's character encoding has not been specified as described in getCharacterEncoding (i.e., the method just returns the default value ISO-8859-1), getWriter updates it to ISO-8859-1.
-
2.2 重定向案例
代码实现
步骤如下:
- 服务器收到客户端请求后,返回状态码302
- 告诉浏览器重定向资源的路径: 响应头location:B资源的路径
//这是我们首先访问的地方
//我们在这里完成重定向
@WebServlet("/responseDemo1")
public class ResponseDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo1的我被访问了");
//这是方法一,比较麻烦,所以我们有方法2
//先来访问我
//1. 设置状态码为302
//response.setStatus(302);
//设置响应头location
//response.setHeader("location","/Login_war_exploded/responseDemo2");
//方法2,相当于对上面的两个步骤进行了封装,简化了这个过程
response.sendRedirect("/Login_war_exploded/responseDemo2");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
//这是我们重定向之后访问的地方
@WebServlet("/responseDemo2")
public class ResponseDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo2的我被访问了");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
重定向特点
- 地址栏发生变化
- 重定向可以访问其他站点的资源
- 重定向是2次请求
- 重定向不能通过request域共享数据
转发的特点:
- 地址栏不发生变化
- 转发只能访问本站的资源
- 转发只是一次请求
- 可以通过request域共享数据
转发: forward 重定向: redirect
2.3 资源路径的写法
分类
- 相对路径: 通过相对路径不可以确定唯一资源 比如:
./index.html
- 不以/开头
- 规则: 确定访问当前资源和目标资源之间的相对位置关系
./
:代表当前目录../
: 代表后退一级的目录
- 绝对路径(推荐使用): 通过绝对路径可以确定唯一资源,比如:
http://localhost:8080/Login_war_exploded/responseDemo2
中的/Login_war_exploded/responseDemo2
- 可以简单的理解为以
/
开头的路径 - 规则: 判断定义的路径是给谁用的? 判断请求将来从哪里发出
- 给客户端浏览器使用: 需要加虚拟目录(项目的访问路径 比如
/Login_war_exploded
) - 给服务器使用: 不需要加上虚拟目录
- 一般来说,转发就不用加上虚拟目录,但是重定向,各种标签的连接要加上虚拟目录的位置
- 给客户端浏览器使用: 需要加虚拟目录(项目的访问路径 比如
- 可以简单的理解为以
关于虚拟目录
虚拟目录在代码中最好不要写死 ,不然当我们虚拟目录发生改变的时候,就会导致我们修改代码十分的麻烦
因此,我们在Servlet类中,我们一般使用request.getContextPath()
这个方法来动态获取我们的虚拟目录
2.4 输出字符数据到浏览器
不多bb,直接上代码,注意事项都在上面
package com.redirect;
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("/responseDemo3")
public class ResponseDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo3的我被访问了");
//设置编码的形式
response.setCharacterEncoding("utf-8");
//我们还可以告诉服务器用什么解码字符集来解码,在请求头中告诉
// response.setHeader("content-type","text/html;charset=utf-8");
//上面这个还有一种简单的写法
response.setContentType("text/html;charset=utf-8");
//获取字符输出流
PrintWriter pw = response.getWriter();
//输出数据,他会自动解析标签
pw.write("<h1>我是你爹</h1>");
//如果我们输入的是中文的内容,那么我们就要注意我们的编码了
//乱码的原因无非就是编码和解码使用的字符集不一致
//输出数据相当于是一个编码的过程,浏览器将数据内容显示相当于是一种解码的过程
//我们在国区用浏览器默认用GBK(gb2312)来解码
//但是tomcat的默认编码是iso-8859-1
//所以我们要设置流的编码(在获取流对象之前)
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
2.5 输出字节数据到浏览器
不多bb,直接上代码
package com.redirect;
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.nio.charset.StandardCharsets;
@WebServlet("/responseDemo4")
public class ResponseDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1 获取字节输出流
ServletOutputStream sos = response.getOutputStream();
//2 获取输出数据,getBytes()默认用GBK编码
sos.write("<h1>我是你爹</h1>".getBytes());
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
2.6 验证码
- 本质:图片
- 出现的目的:防止用户恶意注册,恶意向服务器注入无意义的数据
分析
现在我们要生成随机的验证码并生成到页面上面
要求如图
代码实现
package com.servlet;
import javax.imageio.ImageIO;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
@WebServlet(name = "CheckCodeServlet", value = "/CheckCodeServlet")
public class CheckCodeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int width = 100;
int height = 50;
//创建一个对象,在内存中生成一个图片
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//美化图片
//1 填充背景色
Graphics g = image.getGraphics();
g.setColor(Color.pink);
g.fillRect(0,0,width,height);
//2 画边框
g.setColor(Color.BLUE);
g.drawRect(0,0,width-1,height-1);
//3 写验证码
//3.1 从下面这面生成随机验证码
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random ran = new Random();
for (int i =0;i<=4;i++){
int index = ran.nextInt(str.length());
char c = str.charAt(index);
g.drawString(c + "",width/5*i +20,height/2);
}
//4 画干扰线
g.setColor(Color.GREEN);
//4.1 随机生成坐标点
for (int i =0;i<=8;i++){
int x1 = ran.nextInt(width);
int y1 = ran.nextInt(height);
int x2 = ran.nextInt(width);
int y2 = ran.nextInt(height);
g.drawLine(x1,y1,x2,y2);
}
//将图片输出到页面展示
ImageIO.write(image,"jpg",response.getOutputStream());
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
点击更新验证码
我们还想要用户点击验证码图片来切换验证码,怎么做?
我们要用js来写一些逻辑了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>验证码更新</title>
</head>
<body>
<img id="code" src="/Login_war_exploded/CheckCodeServlet" alt="">
<a id="change" href="#">看不清? 换一张</a>
<script>
<!-- 当用户点击图片的时候我们就要换一张图片-->
window.onload = function(){
let code = document.getElementById("code");
let change = document.getElementById("change");
//我们下面这里下面加一些没有意义的参数的意义是: 浏览器对于图片有缓存,所以图片的地址要有变化才能重新请求
code.addEventListener('click',() => {
let date = new Date().getTime();
code.src = '/Login_war_exploded/CheckCodeServlet?' + date
})
change.addEventListener('click',() => {
let date = new Date().getTime();
code.src = '/Login_war_exploded/CheckCodeServlet?' + date
})
}
</script>
</body>
</html>
代码如图,懂得都懂,就不多解释了
第三章 ServletContext对象
3.1 概述
概念: 代表整个web应用,可以和web应用的容器(Tomcat,服务器)来通信
英文解释: Defines a set of methods that a servlet uses to communicate with its servlet container, for example, to get the MIME type of a file, dispatch requests, or write to a log file.
功能:
- 获取MIME类型: 在互联网通信过程中定义的一种文件数据类型,这个设置在Content-Type里
- 格式: 大类型/小类型
- 例如:
text/html
,image/jpeg
- 他是一个域对象: 可以共享数据
- 获取文件的真实路径(服务器路径)
如何获取?
- 通过request对象获取:
request.getServletContext()
- 通过httpServlet获取:
this.getServletContext()
3.2 获取MIME对象
怎么获取?
方法都是ServletContext类的
-
String getMimeType(String file) Returns the MIME type of the specified file, or null if the MIME type is not known. 这个file参数就是让你输入文件的后缀名,然后这个方法就会返回类似text/html 这个东西
-
//示例代码如下(省略了很多不必要的东西,忽略就好) @WebServlet("/ContextDemo2") public class ContextDemo2 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取MIME类型 ServletContext sc = this.getServletContext(); System.out.println(sc.getMimeType("a.jpg")); System.out.println(sc.getMimeType("a.jpeg")); System.out.println(sc.getMimeType("a.js")); System.out.println(sc.getMimeType("a.html")); //打印结果 //image/jpeg //image/jpeg //application/javascript //text/html } }
-
3.3 域对象
他就像我们之前学过的request
一样,属于一个域对象
所有域对象都有共有的3个方法
-
void setAttribute(String name, Object object) Binds an object to a given attribute name in this ServletContext.
-
Object getAttribute(String name) Returns the servlet container attribute with the given name, or null if there is no attribute by that name.
-
void removeAttribute(String name) Removes the attribute with the given name from this ServletContext.
改对象域对象的范围: 他的范围是最大的,他能共享所有用户所有请求的数据(但是这个不常用,因为他的范围很大,同时生命周期很长【服务器的关闭才会让他销毁】)
//我先访问这个
@WebServlet("/ContextDemo3")
public class ContextDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext sc = this.getServletContext();
sc.setAttribute("Father","我是你爹!");
}
}
//然后再访问这个
@WebServlet("/ContextDemo4")
public class ContextDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext sc = this.getServletContext();
System.out.println(sc.getAttribute("Father"));//打印了我是你爹
}
}
3.4 获取文件真实(服务器)路径
我们都知道,web项目有两个空间。一个是再tomcat服务器里面,一个是叫本地工作空间,用户访问资源的地方自然是服务器的地方,这个地方又叫做文件真实路径
方法:(这个方法很重要)因为当我们要获取这个文件的路径的时候,用的不是本地空间的路径,而是服务器部署的路径,这时候,我们用这个方法也算是动态获取了
String getRealPath(String path)
Gets the real path corresponding to the given virtual path(虚拟路径).
@WebServlet("/ContextDemo5")
public class ContextDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext sc = this.getServletContext();
//注意 login.html是在我的webapp文件夹的目录下的文件
System.out.println(sc.getRealPath("/login.html"));//C:\Users\CUMT_CJY\Desktop\Web\Login\target\Login-1.0-SNAPSHOT\login.html
}
}
第四章 文件下载案例
4.1 案例分析
文件下载需求:
- 页面显示下载的超链接
- 超链接如果指向资源:(但是这不满足我们的需求)
- 能被解析的资源就会直接在浏览器中展示,不能被解析的就提示下载
- 超链接指向Servlet类(用这个,在类里面我们可以定义响应头等)
- 超链接如果指向资源:(但是这不满足我们的需求)
- 点击超链接之后弹出一个下载提示框
- 完成文件的下载
步骤:
- 定义页面,编辑超链接的href属性,指向我们定义的Servlet,连接的参数传递资源的名称filename
- 定义Servlet
- 获取文件名称
- 加载文件进内存
- 使用ServletContext获取到文件的真实路径
- 用字节输入流加载文件进内存
- 指定response的响应头:
content-disposition:attachment;filename=xxx
- 新建输出流,用write写文件
4.2代码实现
网页
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="/Login_war_exploded/servletdownload?filename=download.jpg">文件下载</a>
</body>
</html>
servlet实现类
package com.download;
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("/servletdownload")
public class ServletDownLoad extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext sc = this.getServletContext();
//获取参数
String filename = request.getParameter("filename");
//获取文件的真实(服务器)路径
String realPath = sc.getRealPath("/images/" + filename);
//使用字节流来关联
FileInputStream fis = new FileInputStream(realPath);
//设置响应头
String mimeType = sc.getMimeType(filename);
response.setContentType(mimeType);
response.setHeader("content-disposition","attachment;filename=" + filename);
//将输入流的数据写出到输出流中
ServletOutputStream sos = response.getOutputStream();
byte[] buff = new byte[1024 * 8];
int len = 0;
while ((len = fis.read(buff)) != -1){
sos.write(buff,0,len);
}
fis.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
4.3 文件中文问题
解决思路
- 获取客户端使用的浏览器的版本信息
- 根据不用的版本信息,设置filename的编码方式
这里直接上一个工具类截图
agent就是请求头里面的user-agent