【HTTP 协议基础】
一、 HTTP 协议概述
概念:Hyper Text Transfer Protocol 超文本传输协议
传输协议:定义了客户端和服务器端通信时,发送数据的格式
1. 特点
- 基于 TCP/IP 的高级协议
- 默认端口号:80
- 基于请求/响应模型:一次请求对应一次响应
- 无状态的:每次请求之间相互独立,不能交互数据
2. 历史版本
区别(最大区分点):
- HTTP 1.0 版本:每一次请求响应都会建立新的连接
- HTTP 1.1 版本:可以复用连接(当每一个响应过后,会等待一段时间,如果仍有数据响应,继续复用上一个请求得到的连接,直到响应超时,没有响应)
二、 请求消息
客户端发送给服务器端的数据
1. 请求消息数据格式
(1) 请求行
- 请求方式:
- HTTP 协议中有7中请求方式,常用的有2种
- GET:
- 请求参数在请求行中,在 URL 后
- 请求的 URL 长度有限制
- 不太安全
- POST:
- 请求参数在请求体中
- 请求的 URL 长度没有限制
- 相对安全
- GET:
- HTTP 协议中有7中请求方式,常用的有2种
请求方式 | 请求 URL | 请求协议/版本 |
---|---|---|
GET 或 POST | /login.html(资源路径) | HTTP/1.1 |
(2) 请求头
客户端浏览器告诉服务器一些信息
- 常见的请求头:
- User-Agent :浏览器告诉服务器,访问该信息使用的浏览器版本信息
- 可以在服务器端获取该头的信息,解决浏览器的兼容性问题
- Referer :http://localhost/login.html
- 告诉服务器:我当前的请求从哪里来
- 作用:① 防盗链 ②统计工作
- 告诉服务器:我当前的请求从哪里来
- User-Agent :浏览器告诉服务器,访问该信息使用的浏览器版本信息
请求头名称 | 请求头值 |
---|
① 防盗链
举例:
你使用正版视频网站的首页跳转到被电影出版商认证的你选择播放的电影页面,可以正常播放
如果你自己写出一个盗版的视频网站首页,采用盗链的方式也可以跳转到该认证播放地址中选择的电影播放页面,这就是盗链行为
如果官方在播放页面中加入 Referer 判断,使得非授权授权页面主页跳转到此播放地址的行为无法播放,这就是防盗链
② 统计工作
类似的:如果网站进行广告推广,可以使用 Referer 统计,哪个广告位跳转到该页面的次数多,进行定向投资广告业务
(3) 请求空行
空行,用于分割 POST 请求的请求头和请求体
空行 |
---|
(4) 请求体(正文)
请求体(正文)是用来封装 POST 请求消息的请求参数
(5) 请求字符串格式
空行之后属于请求体
POST /login.html HTTP/1.1
Host: localhost
User-Agent: ……
Accept: ……
Accept-Language: ……
Accept-Encoding: ……
Referer: http://localhost/login.html
Connection: ……
Upgrade-Insecure-Requests: ……
username=zhangsan
2. Request 对象和 Response 对象原理
- Request 对象和 Response 对象是由服务器创建的,我们来使用它们
- Request 对象是用来获取请求消息的, Response 对象是用来设置响应消息的
工作原理过程:
客户端浏览器通过:http://localhost:端口号/虚拟目录/资源路径
向服务器请求发出请求消息:之后
- Tomcat 服务器会根据请求 URL 中的资源路径,(以创建 ServletTest 类并实现 Servlet 接口重写方法的实现类为例)创建对应的 ServletTest 对象
- Tomcat 服务器,会创建 Request 对象和 Response 对象,Request 对象中封装请求消息数据
- Tomcat 服务器将 Request 对象和 Response 对象传递给 service 方法,并且调用 service 方法
在创建的 ServletTest 类并实现 Servlet 接口重写方法的实现类中:之后
- 程序员可以通过 Request 对象获取请求消息数据,通过 Response 对象设置响应消息数据
之后服务器向客户端浏览器响应,进行响应消息:之后
- 服务器在浏览器做出响应之前,会从 Response 对象中拿程序员设置的响应消息数据
3. Request 对象继承体系结构
4. Request 对象的功能
-
获取请求消息数据
- 获取请求行数据
- 获取请求头数据
- 获取请求体数据
-
其他功能
(1) 获取请求消息数据
① 获取请求行数据
- 请求格式:GET /test_tomcat/test01?name=zhangsan HTTP/1.1
- 方法:
- 获取请求方式:GET :
String getMethod()
- 获取虚拟目录:/test_tomcat :
String getContextPath()
- 获取 Servlet 路径:/test01 :
String getServletPath()
- 获取 get 方式请求参数:name=zhangsan :
String getQueryString()
- 获取请求 URI:/test_tomcat/test01 :
String getRequestURI()
- 获取请求 URL:http://localhost/test_tomcat/test01 :
StringBuffer getRequestURL()
- 获取协议以版本:HTTP/1.1 :
String getProtocol()
- 获取客户据的 IP 地址:
String getRemoteAddr()
- 获取请求方式:GET :
URL :统一资源定位符
URI :统一资源标识符(范围较大)
代码演示:
- 可以直接按照下图中直接创建 Servlet 类,默认继承 HttpServlet 抽象类,并进行方法重写,我们这里只使用
doGet()
方法
@WebServlet(name = "ServletTest07", value = "/ServletTest07")
public class ServletTest07 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取请求方式 GET
String method = request.getMethod();
System.out.println(method);
// 2. 获取虚拟目录 /tomcat_test
String contextPath = request.getContextPath();
System.out.println(contextPath);
// 3. 获取 Servlet 路径 /ServletTest07
String servletPath = request.getServletPath();
System.out.println(servletPath);
// 4. 获取 get 方式请求参数 name=zhangsan
String queryString = request.getQueryString();
System.out.println(queryString);
// 5. 获取请求 URI /tomcat_test/ServletTest07
String requestURI = request.getRequestURI();
System.out.println(requestURI);
// 6. 获取请求 URL http://localhost:8080/tomcat_test/ServletTest07
StringBuffer requestURL = request.getRequestURL();
System.out.println(requestURL);
// 7. 获取协议及版本 HTTP/1.1
String protocol = request.getProtocol();
System.out.println(protocol);
// 8. 获取客户机的 IP 地址 0:0:0:0:0:0:0:1(由于是自己访问自己)
String remoteAddr = request.getRemoteAddr();
System.out.println(remoteAddr);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
② 获取请求头数据
- 方法:
String getHeader(String name)
: 通过请求头的名称获取请求头的值Enumeration<String> getHeaderNames()
:获取所有的请求头名称
代码演示:
- 获取所有的请求头名称
@WebServlet(name = "ServletTest08", value = "/ServletTest08")
public class ServletTest08 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取所有请求头名称
Enumeration<String> headerNames = request.getHeaderNames();
// 遍历
while (headerNames.hasMoreElements()){
String name = headerNames.nextElement();
// 根据名称获取请求头的值
String value = request.getHeader(name);
System.out.println(name+"---"+value);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
- 获取请求头数据:User-Agent
@WebServlet(name = "ServletTest09", value = "/ServletTest09")
public class ServletTest09 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 演示获取请求头数据:user-agent 不区分大小写
String agent = request.getHeader("User-Agent");
// 判断 agent 的浏览器版本
if (agent.contains("Chrome")){
System.out.println("谷歌来了!");
}else if (agent.contains("Firefox")){
System.out.println("火狐来了!");
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
- 演示获取头数据:Referer ,并且判断反应是否有盗链行为
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 演示获取头数据:referer
String referer = request.getHeader("Referer");
System.out.println(referer);
// 防盗链
if (referer!=null){
if (referer.contains("tomcat_test")){
// 是否直接通过浏览器的规定虚拟目录位置访问
// 正常访问
System.out.println("正常访问!");
// 浏览器页面显示
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("正常访问!");
}else {
// 盗链
System.out.println("盗链!");
// 浏览器页面显示
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("盗链行为!");
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>主页</title>
</head>
<body>
<a href="http://localhost:8080/tomcat_test/ServletTest09">正常访问</a>
</body>
</html>
③ 获取请求体数据
- 请求体:只有 POST 请求方式中,才有请求体,在请求体中封装了 POST 请求的请求参数
- 步骤:
- 获取流对象
BufferReader getReader()
:获取字符输入流,只能操作字符数据ServletInputStream getInputStream()
:获取字节输入流,可以操作所有数据类型
- 再从流对象中拿数据
- 获取流对象
代码演示:
@WebServlet(name = "ServletTest10", value = "/ServletTest10")
public class ServletTest10 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取请求消息体:请求参数
// 获取字符流
BufferedReader reader = request.getReader();
// 读取数据
String line=null;
while((line= reader.readLine())!=null){
System.out.println(line);
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="http://localhost:8080/tomcat_test/ServletTest10" method="post">
<input type="text" placeholder="请输入用户名" name="username"><br>
<input type="text" placeholder="请输入密码" name="password"><br>
<input type="submit" value="注册">
</form>
</body>
</html>
(2) 其他功能
① 获取请求参数的通用方式
不论是 GET 还是 POST 请求方式都可以使用下列方法来获取请求参数
String getParameter(String name)
:根据参数名称获取参数值(username=zhangsan&password=123456)String[] getParameterValues(String name)
:根据参数名称获取参数值的数组(hobby=xxxx&hobby=game)Enumeration<String> getParameterNames()
:获取所有请求的参数名称Map<String,String[]> getParameterMap()
:获取所有参数的 Map 集合
代码演示:第一个方法
@WebServlet(name = "ServletTest11", value = "/ServletTest11")
public class ServletTest11 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// get 获取请求参数
// 根据参数名称获取参数值
String username = request.getParameter("username");
System.out.println("get");
System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// post 获取请求参数
// 根据参数名称获取参数值
// String username = request.getParameter("username");
// System.out.println("post");
// System.out.println(username);
// 既然两个方法的业务逻辑相同,没必要再写一遍,只需要调用即可
this.doGet(request,response);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="http://localhost:8080/tomcat_test/ServletTest11" method="post">
<input type="text" placeholder="请输入用户名" name="username"><br