HTTP响应详解(注意以下内容是响应的,也穿插一些请求的,上一篇是请求的,)
①**++状态码:++状态码就是服务器给客户端的一个数字代号**,用来告诉对方 "这次请求结果怎么样"。
它一般由3
- 1xx:信息类,请求还在处理
- 2xx:成功类,请求正常完成(最常见:200 OK)
- 3xx:重定向类,页面 / 地址变了(如 301、302)
- 4xx:客户端错误,你这边有问题(如 404 找不到、403 禁止访问)
- 5xx:服务器错误,服务端挂了 / 出错了(如 500 服务器内部错误)
最常见的几个状态码:
- 200:请求成功,一切正常
- 301:永久跳转:
这个地址彻底不用了,永久搬到新地址
浏览器、搜索引擎都会记住新地址,以后直接访问新的
例子:网站换域名,旧网址永久指向新网址
- 302:临时跳转:
只是暂时换个地址,以后还会变回来
搜索引擎不会把权重转移,只是这次帮你跳过去
例子:未登录时临时跳转到登录页
- 400:请求参数错误
你发给服务器的数据格式不对、参数缺了、乱填了
服务器看不懂你的请求,直接拒绝
不是服务器问题,是你发的内容有问题
- 401:未登录 / 没权限
你没登录、登录过期、token 无效
服务器:我不知道你是谁,不让你进
一般会跳登录页或要求重新登录
- 403:禁止访问
你登录了,但没权限
服务器:我认识你,但你不能看这个内容
比如:普通用户访问管理员页面、IP 被拉黑
- 404:页面不存在
你访问的地址根本没有
可能是 URL 写错、接口删了、路径不对
服务器:我这儿没这个东西
- **405:**请求方法不被允许
地址是对的,但用错了请求方式
接口只允许 GET,你用了 POST
接口只允许 POST,你用了 GET
服务器:路是对的,但你不能这么走
- 500:服务器内部报错
代码崩了、空指针、数据库挂了、逻辑异常
是服务器端代码 / 环境出问题,不是客户端的错
后端程序员要去看日志修复
- 502:网关错误
网关(Nginx / 代理)收到后端返回的无效响应
后端服务挂了、崩了、重启中、无法正常工作
网关:后端坏了,我没法给你正常结果
- 504:中间服务器等不到后端服务器的响应,超时了
网关把请求发给后端
后端处理太慢,一直不返回结果
网关等超时了,直接断开
典型原因:慢查询、死循环、接口执行太久
②++响应头:++
响应头的基本格式和请求头的格式基本⼀致
常见响应头(Response Headers)
1. 内容类型相关
Content-Type 告诉浏览器返回内容是什么格式。例:
text/html、application/json、image/pngContent-Length返回内容的长度(字节数)
Content-Encoding 内容压缩方式,如
gzip
2. 缓存控制
Cache-Control 控制浏览器要不要缓存、缓存多久例:
no-cache、max-age=3600Expires缓存过期时间
ETag / Last-Modified用于缓存校验,判断内容是否变化
3. 跨域相关(前后端分离必见)
Access-Control-Allow-Origin允许哪些域名跨域访问
Access-Control-Allow-Methods允许哪些请求方法:GET、POST、PUT...
Access-Control-Allow-Headers允许哪些请求头
4. 跳转相关
- Location配合 301/302 使用,跳转目标地址
5. Cookie 相关
- Set-Cookie服务器种下 Cookie,浏览器会自动保存并下次带上
6. 安全相关
X-Frame-Options防止页面被嵌套 iframe(防点击劫持)
X-XSS-Protection开启浏览器 XSS 防护
Content-Security-Policy内容安全策略,限制资源加载
7. 服务器信息
Server服务器软件,如 Nginx、Apache、Tomcat
Date响应时间
③++响应正文(body):++
响应 Body 就是服务器真正返回给你的 "内容本身"。
1. HTML 格式(网页)
类型:
text/html样子:一整段网页代码
<html> <body>你好
</body> </html>
2. JSON 格式(接口最常用)
类型:
application/json样子:键值对结构,前后端交互标配
{
"code": 200,
"msg": "成功",
"data": { "name": "张三" }
}
3. 纯文本格式
类型:
text/plain样子:就是普通字符串
hello world
登录成功
4. XML 格式
类型:
application/xml样子:标签包裹数据
<user> <name>张三</name> <age>18</age> </user>
5. JavaScript 格式
类型:
application/javascript样子:一段 JS 代码
alert('hello');
6. CSS 格式
类型:
text/css样子:样式代码
body { color: red; }
7. 图片 / 文件(二进制)
- 类型:
image/png、image/jpeg、application/octet-stream- 样子:肉眼看不懂的二进制数据流,浏览器会自动渲染成图片 / 文件
必须背的(高频)
- application/json接口、前后端交互,几乎全是它
- text/html网页页面
- application/x-www-form-urlencoded普通表单提交(key=value&a=b)
- multipart/form-data上传文件、图片用这个
眼熟就行,不用死记
- text/plain:纯文本
- text/css:样式表
- application/javascript:JS 文件
- image/jpeg、image/png:图片
- application/octet-stream:二进制文件
考试 / 面试怎么考?
一般只问:
- 接口用什么?→ application/json
- 上传文件用什么?→ multipart/form-data
- 网页是什么?→ text/html
④**++通过form表单构造HTTP请求:++**
意思是:用 HTML 的 <form> 标签,让浏览器自动帮你组装并发送一个 HTTP 请求
form 的重要参数
-
action: 构造的 HTTP 请求的 URL 是什么。
-
method: 构造的 HTTP 请求的方法是 GET 还是 POST,form 只支持 GET 和 POST。
input 的重要参数
-
type: 表示输入框的类型。text 表示文本,password 表示密码,submit 表示提交按钮。
-
name: 表示构造出的 HTTP 请求的 query string 的 key。query string 的 value 就是输入框的用户输入的内容。
-
value: input 标签的值。对于 type 为 submit 类型来说,value 就对应了按钮上显示的文本。
html
<!-- 1. form 标签:定义请求的"地址"和"方法" -->
<form action="http://abcdef.com/myPath" method="GET">
<!-- 2. 第一个输入框:告诉服务器参数名是 userId -->
<!-- 用户输入什么,value 就是什么 -->
<input type="text" name="userId">
<!-- 3. 第二个输入框:告诉服务器参数名是 classId -->
<input type="text" name="classId">
<!-- 4. 提交按钮:点击它,浏览器才会自动发请求 -->
<!-- value="提交" 是按钮上显示的文字 -->
<input type="submit" value="提交">
</form>
这几行代码的作用 = 做一个能让用户输入内容、并把数据发给服务器的表单。
然后就可以得到我们前面提到的:请求数据包
html
GET http://abcdef.com/myPath?userId=100&classId=200 HTTP/1.1
Host: abcdef.com
Proxy-Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/w
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
1. 让用户能输入内容(显示两个输入框)
代码会在网页上显示:
- 一个输入框:让用户填 userId(学号 / 用户 ID)
- 一个输入框:让用户填 classId(班级 ID)
- 一个提交按钮
用户可以在里面打字
2. 收集用户输入的数据
你写:
html
预览
name="userId"
name="classId"
作用就是:告诉浏览器:把用户输入的内容,分别记成 userId 和 classId。
3. 点击提交 → 自动把数据发给服务器
这是最重要的作用:用户点提交,浏览器会自动:
- 拼接成网址
- 把 userId、classId 带给服务器
- 向
http://abcdef.com/myPath发送请求
例如用户输入:
- userId = 100
- classId = 20
浏览器就会访问:
http://abcdef.com/myPath?userId=100&classId=20
这就是把数据传给后端(服务器)。
查询、登录、注册、提交表单 全是靠这个实现的
⑤++form发送POST请求:++
html
<form action="http://abcdef.com/myPath" method="POST">
<input type="text" name="userId">
<input type="text" name="classId">
<input type="submit" value="提交">
</form>
跟刚刚的代码只改了一个地方:method="GET" → method="POST"
然后就得到请求数据包:
html
POST http://abcdef.com/myPath HTTP/1.1
Host: abcdef.com
Proxy-Connection: keep-alive
Content-Length: 22
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/w
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
userId=100&classId=200
改完后发生了什么?
-
GET :数据拼在网址里 → 带
?``http://abcdef.com/myPath?userId=1001&classId=303 -
POST :数据放在 ** 请求体(body)** 里 → 网址干干净净,没有问号!
http://abcdef.com/myPath
⑥++通过ajax构造HTTP请求:++
从前端角度,除了浏览器地址栏能构造 GET 请求,form 表单能构造 GET 和 POST 之外,还可以通过 ajax 的方式来构造 HTTP 请求,并且功能更强大。
ajax 全称 Asynchronous Javascript And XML,是 2005 年提出的一种 JavaScript 给服务器发送 HTTP 请求的方式。
特点是可以不需要刷新页面、不需要页面跳转就能进行数据传输。
在 JavaScript 中可以通过 ajax 的方式构造 HTTP 请求。
⑦++发送GET请求:++
即向服务器 "要数据",并且把参数直接放在网址里。
javascript
<!-- 引入 jquery -->
<script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>
$.ajax({
type: 'get',
url: 'https://www.sogou.com?studentName=zhangsan',
success: function(data) {
console.log("当服务器返回的响应到达浏览器之后,浏览器触发该回调,通知到咱们")
}
});
console.log("浏览器立即往下执行后续代码");
这是一段用 jQuery 封装的 AJAX 代码,作用是:
用 JavaScript 代码,手动发送一个 GET 请求 ,而且不刷新页面,异步拿到服务器的响应。
① type: 'get'
- 作用:指定请求方法是 GET。
- 对应你之前学的:GET 就是向服务器要数据,参数拼在 URL 里。
② url: 'https://www.sogou.com?studentName=zhangsan'
- 作用:指定请求的目标地址。
?studentName=zhangsan就是 GET 请求的参数,和你之前 form 表单 GET 生成的 URL 完全一样。
③ success: function(data) { ... }
- 这是回调函数,是 AJAX 「异步」的核心!
- 执行时机:等服务器把响应完整发回来之后,浏览器才会执行这个函数。
data参数:就是服务器返回的响应 Body(正文),比如搜狗的 HTML 页面代码。- 控制台会打印:
当服务器返回的响应到达浏览器之后,浏览器触发该回调,通知到咱们
④ 最后一行 console.log("浏览器立即往下执行后续代码");
- 执行时机:AJAX 请求发出去之后,浏览器不会等服务器响应,直接立刻执行这一行。
- 控制台会打印:
浏览器立即往下执行后续代码
「异步」到底是什么意思?
这是这段代码的核心考点,我用一句话讲透:
AJAX 发请求是「后台悄悄发」,浏览器不会卡住等响应,而是继续执行后面的代码,等响应回来了再执行回调函数。
执行顺序(控制台打印顺序):
- 先打印:
浏览器立即往下执行后续代码(请求发出去,不等响应,直接执行下一行)- 等几秒(网络延迟),服务器响应回来后,再打印:
当服务器返回的响应到达浏览器之后,浏览器触发该回调,通知到咱们对比「同步」(你平时的代码):
- 同步代码:一行一行执行,上一行没执行完,下一行绝对不跑。
- AJAX 异步:发请求的同时,继续跑后面的代码,响应回来再补执行回调。
⑧++发送POST请求:++
对于POST请求,需要设置body的内容
1.先使用 setRequestHeader 设置 Content-Type
2.再通过 send 的参数设置 body 内容
⬇️发送 application/x-www-form-urlencoded 数据,数据格式同 form 的 post
javascript
<!-- 引入 jquery -->
<script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>
$.ajax({
type: 'post',
url: 'https://www.sogou.com',
contentType: 'application/x-www-form-urlencoded',
data: 'studentName=zhangsan',
success: function(data) {
// data 则是响应的正文部分
console.log("当服务器返回的响应到达浏览器之后,浏览器触发该回调,通知到咱们")
}
});
console.log("浏览器立即往下执行后续代码");
① type: 'post'
- 作用:指定请求方法是 POST。
- 对应知识点:POST 会把数据放在 ** 请求体(Body)** 里,URL 干干净净,不会出现
?,和form表单method="POST"效果完全一致。
② url: 'https://www.sogou.com'
- 作用:指定请求的目标服务器地址。
- 注意:这里 URL 里没有
?和参数,因为 POST 的参数放在请求体里,不是拼在 URL 里。
③ contentType: 'application/x-www-form-urlencoded'
- 作用:设置请求头
Content-Type,告诉服务器:我发的数据是「表单格式」。 - 对应知识点:和
form表单 POST 提交时,浏览器自动设置的Content-Type完全一样,所以说「数据格式同 form 的 post」。
④ data: 'studentName=zhangsan'
- 作用:设置请求体(Body)的内容。
- 格式:
key=value,多个参数用&连接(比如studentName=zhangsan&age=20),和form表单 POST 提交的数据格式 100% 相同。
⑤ success: function(data) { ... }
- 这是回调函数,是 AJAX「异步」的核心!
- 执行时机:等服务器把响应完整发回来之后,浏览器才会执行这个函数。
data参数:就是服务器返回的响应 Body(正文),比如搜狗的 HTML 页面代码。- 控制台会打印:
当服务器返回的响应到达浏览器之后,浏览器触发该回调,通知到咱们
⑥ 最后一行 console.log("浏览器立即往下执行后续代码");
- 执行时机:AJAX 请求发出去之后,浏览器不会等服务器响应,直接立刻执行这一行。
- 控制台会打印:
浏览器立即往下执行后续代码
主要用途:登录/上传各种文件
⑨++通过 Java socket 构造 HTTP 请求:++
即用 Java 语言里的 Socket 技术,手动拼接、发送一个 HTTP 请求数据包
"发送HTTP请求",本质上就是按照HTTP的格式往TCP Socket中写入一个字符串。
"接受HTTP响应",本质上就是从TCP Socket中读取一个字符串,再按照HTTP的格式来解析。
所以我们基于Socket的知识,完全可以构造出一个简单的HTTP客户端程序,用来发送各种类型的HTTP请求。
java
那下面这个解释一下是干什么的
public class HttpClient {
private Socket socket;
private String ip;
private int port;
public HttpClient(String ip, int port) throws IOException {
this.ip = ip;
this.port = port;
socket = new Socket(ip, port);
}
public String get(String url) throws IOException {
StringBuilder request = new StringBuilder();
// 构造⾸⾏
request.append("GET " + url + " HTTP/1.1\n");
// 构造 header
request.append("Host: " + ip + ":" + port + "\n");
// 构造 空⾏
request.append("\n");
// 发送数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write(request.toString().getBytes());
// 读取响应数据
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[1024 * 1024];
int n = inputStream.read(buffer);
return new String(buffer, 0, n, "utf-8");
}
public String post(String url, String body) throws IOException {
StringBuilder request = new StringBuilder();
// 构造⾸⾏
request.append("POST " + url + " HTTP/1.1\n");
// 构造 header
request.append("Host: " + ip + ":" + port + "\n");
request.append("Content-Length: " + body.getBytes().length + "\n");
request.append("Content-Type: text/plain\n");
// 构造 空⾏
request.append("\n");
// 构造 body
request.append(body);
// 发送数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write(request.toString().getBytes());
// 读取响应数据
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[1024 * 1024];
int n = inputStream.read(buffer);
return new String(buffer, 0, n, "utf-8");
}
public static void main(String[] args) throws IOException {
HttpClient httpClient = new HttpClient("42.192.83.143", 8080);
String getResp = httpClient.get("/AjaxMockServer/info");
System.out.println(getResp);
String postResp = httpClient.post("/AjaxMockServer/info", "this is body"
System.out.println(postResp);
}
}
这是一个用 Java Socket 手写的迷你版浏览器(迷你 HTTP 客户端)( 就是用java模仿出get和post的请求数据包 然后发出**)**
能手动发送 GET 和 POST 请求,不用浏览器,不用 AJAX,纯 Java 代码发 HTTP 请求。
(1)
java
// 拿到"往外发"的管道
OutputStream outputStream = socket.getOutputStream();
// 把你拼好的 HTTP 请求数据包,发出去
outputStream.write(request.toString().getBytes());
→ 这一步:发送数据(请求)给服务器
(2)
java
// 拿到"往里收"的管道
InputStream inputStream = socket.getInputStream();
// 开辟一块空间存服务器返回的数据
byte[] buffer = new byte[1024 * 1024];
// 读取响应数据
int n = inputStream.read(buffer);
// 转成字符串返回
return new String(buffer, 0, n, "utf-8");
→ 这一步:接收服务器返回的数据(响应),并返回出去
1. 面试 / 考试 非常重要
因为它考的是:
- HTTP 请求到底长啥样
- 请求行、请求头、请求体分别是什么
- 请求和响应的区别
- GET 和 POST 的本质区别
这些是计算机网络必考内容。
2. 真实工作中 几乎不用手写
真实开发里,我们都用成熟工具:
- OkHttp
- RestTemplate
- HttpClient
- Feign
⑩++HTTPS&加密++
HTTPS 也是一个应用层协议,是在 HTTP 协议的基础上引入了一个加密层(HTTP+S)。
HTTP 协议内容都是按照文本的方式明文传输的,这就导致在传输过程中出现一些被篡改的情况。
因为我们发送/接收的数据包都是经过运营商的网络设备,所以运营商的有能力解析出数据包中的内容并对其进行篡改。除了运营商,一些黑客也能进行劫持和篡改
所以HTTPS在HTTP基础上进行了加密
加密就是把明文 (要传输的信息) 进行一系列变换,生成密文。
解密就是把密文再进行一系列变换,还原成明文。
其中加密解密都需要使用到密钥(可以不是同一个密钥)
加密的方式有两种:对称加密 和 非对称加密
(1)对称加密: 加密/解密使用同一个密钥(这把密钥必须双方都保密,不能让任何第三方知道)
速度快、效率高,适合大量数据加密
(2)非对称加密:加密使用一个密钥 | 解密使用另一个密钥(公钥可以公开随便发,谁都能拿到;私钥 必须严格保密)
公钥加密 → 只能私钥解密
私钥加密 → 只能公钥解密
速度慢,不适合加密大文件
它们的核心区别只在:有几把钥匙、怎么用
-
对称加密
- 只有1 把钥匙
- 加密 = 这把钥匙
- 解密 = 还是这把钥匙→ 泄露 = 全军覆没
-
非对称加密
- 有2 把钥匙:公钥 + 私钥
- 公钥:随便公开,不怕泄露
- 私钥:必须保密,泄露就完蛋→ 公钥丢了没事,私钥丢了才完蛋
非对称加密是怎么提高安全性的(++理论双向非对称加密,实际上没有用到客户端的公钥私钥++)
- 公钥:公开,随便看
- 私钥:保密,客户端和服务器都有++不同的私钥++
- 客户端给服务器发数据:用服务器的公钥 B 加密 → 只有服务器的私钥 B能解开
- 服务器给客户端发数据:用客户端的公钥 A 加密 → 只有客户端的私钥 A能解开
但是这个系统有一个致命问题:客户端拿到的公钥 B,无法保证100% 是真实服务器的公钥
所以需要引入证书来解决
(3)证书:
数字证书是由权威 CA 机构颁发的、绑定了服务器公钥与身份信息 的电子文件,核心作用是证明公钥的真实归属,防止中间人冒充,是 HTTPS 安全体系的核心信任基石。
HTTPS完整流程
步骤 1:服务器持证,准备通信
服务器先生成一对「钥匙」:公钥 B(公开的锁)、私钥 B(只有服务器自己有的钥匙) 。然后它拿着自己的身份信息、公钥 B,去找权威 CA 机构(相当于互联网的「公安局」),申请一张数字身份证(数字证书)。这张证书里,写了服务器的公钥 B、服务器的域名、还有 CA 给盖的「防伪公章(CA 签名)」。服务器把私钥 B 牢牢锁在自己手里,后面专门用来开锁解密。
步骤 2:客户端验签,确认公钥可信
你(客户端)要连服务器,服务器把自己的「数字身份证(证书)」发给你。你电脑 / 手机系统里,早就预装了所有权威 CA 的「公钥(相当于公安局的公开验章工具)」,你用这个工具,去验证书上的「防伪公章」:
- 这个公章是 CA 用自己的私钥盖的,只有 CA 的公钥能打开验证。
- 能顺利打开,而且打开后看到的服务器信息(公钥 B、域名)和证书上写的一模一样,就说明这张身份证是真的,公钥 B 确实是目标服务器的,黑客造不了假。
- 要是验不过,直接断开连接,提醒你「这网站不安全」。👉 这一步就是客户端第一次解密:用 CA 公钥解密 CA 的签名,验明正身。
步骤 3:安全加密,传输对称密钥
你自己随便生成一个临时密码(对称密钥),这个密码跟服务器、客户端的公私钥完全没关系,就是你这次聊天专属的密码。你用刚才验过真的、服务器的公钥 B(公开的锁),把这个临时密码锁起来,发给服务器。
- 黑客就算截到了这个锁着的密码,没有服务器的私钥 B(唯一的钥匙),根本打不开,拿不到密码。
- 而且公钥 B 已经验过是真的,黑客没法用假公钥骗你,绝对安全。
步骤 4:服务器解密,建立安全通道
服务器用自己的私钥 B,把你发的锁着的临时密码打开,拿到了这个密码。后面你和服务器所有的聊天、传数据,都用这个同一个临时密码来加密解密:
- 服务器给你发数据,用这个密码锁上发给你;
- 你用同一个密码打开(第二次解密),就能看到内容;
- 你给服务器发数据,也用这个密码锁上,服务器用同一个密码打开。非对称加密慢,只用来传一次密码;后面用对称加密,又快又安全,双向聊天就彻底安全了。
注意:引入证书后
实际用到的
- 服务器公钥
- 服务器私钥
完全没用到、也不存在的
- 客户端公钥
- 客户端私钥
最后:黑客可以截、可以改、可以换证书,但永远无法伪造合法证书。 证书一验就穿帮,所以 HTTPS 是安全的。