HTTP协议基础
HTTP(HyperText Transfer Protocol)是用于传输超文本的应用层协议,它是万维网数据通信的基础。HTTP协议基于TCP/IP协议,默认端口为80(HTTPS为443)。
HTTP工作原理
- 客户端(通常是浏览器)向服务器发送HTTP请求
- 服务器接收请求并处理
- 服务器返回HTTP响应
- 客户端接收响应并处理
常见的HTTP请求方法
| 方法 | 描述 |
|---|---|
| GET | 请求指定的资源,只用于获取数据 |
| POST | 向指定资源提交数据,通常会导致服务器状态变化 |
| PUT | 替换目标资源的所有当前表示 |
| DELETE | 删除指定的资源 |
| HEAD | 类似于GET请求,但只返回响应头,不返回响应体 |
| PATCH | 对资源进行部分修改 |
| OPTIONS | 返回服务器支持的HTTP方法 |
| CONNECT | 建立到目标资源的隧道 |
| TRACE | 回显服务器收到的请求,主要用于测试或诊断 |
HTTP请求结构
一个典型的HTTP请求包含以下部分:
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
- 请求行:方法 + URL + HTTP版本
- 请求头:多个键值对
- 空行
- 请求体(可选)
HTTP响应结构
一个典型的HTTP响应包含以下部分:
HTTP/1.1 200 OK
Date: Mon, 23 May 2022 22:38:34 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 138
Last-Modified: Wed, 08 Jan 2022 23:11:55 GMT
Server: Apache/2.4.1 (Unix)
<html>
<head>
<title>Example Page</title>
</head>
<body>
<p>Hello World!</p>
</body>
</html>
- 状态行:HTTP版本 + 状态码 + 状态消息
- 响应头:多个键值对
- 空行
- 响应体
常见的HTTP请求头
| 请求头 | 描述 |
|---|---|
| Accept | 客户端能够接收的内容类型 |
| Accept-Charset | 客户端能够显示的字符集 |
| Accept-Encoding | 客户端能够处理的编码方式 |
| Accept-Language | 客户端期望的语言 |
| Authorization | 认证信息 |
| Cache-Control | 缓存机制 |
| Connection | 连接方式(keep-alive等) |
| Content-Length | 请求体的长度 |
| Content-Type | 请求体的MIME类型 |
| Cookie | 客户端cookie |
| Host | 请求的服务器域名和端口号 |
| Referer | 请求来源页面的URL |
| User-Agent | 客户端信息(浏览器类型、版本等) |
| If-Modified-Since | 资源修改时间比对 |
| If-None-Match | ETag比对 |
常见的HTTP响应头
| 响应头 | 描述 |
|---|---|
| Access-Control-Allow-Origin | 指定哪些网站可以跨域资源共享 |
| Cache-Control | 缓存机制 |
| Content-Encoding | 响应体的编码方式 |
| Content-Length | 响应体的长度 |
| Content-Type | 响应体的MIME类型 |
| Date | 响应生成的日期和时间 |
| ETag | 资源的特定版本标识符 |
| Expires | 响应过期时间 |
| Last-Modified | 资源最后修改时间 |
| Location | 重定向目标URL |
| Server | 服务器信息 |
| Set-Cookie | 设置HTTP Cookie |
| Status | HTTP状态码 |
HTTP状态码
HTTP状态码分为5类:
1xx 信息性状态码
- 100 Continue:客户端应继续请求
- 101 Switching Protocols:服务器同意切换协议
2xx 成功状态码
- 200 OK:请求成功
- 201 Created:资源创建成功
- 202 Accepted:请求已接受但未处理完成
- 204 No Content:请求成功但无内容返回
3xx 重定向状态码
- 301 Moved Permanently:资源永久移动
- 302 Found:资源临时移动
- 304 Not Modified:资源未修改(使用缓存)
4xx 客户端错误状态码
- 400 Bad Request:请求语法错误
- 401 Unauthorized:需要认证
- 403 Forbidden:服务器拒绝请求
- 404 Not Found:资源不存在
- 405 Method Not Allowed:请求方法不被允许
5xx 服务器错误状态码
- 500 Internal Server Error:服务器内部错误
- 502 Bad Gateway:网关错误
- 503 Service Unavailable:服务不可用
- 504 Gateway Timeout:网关超时
XMLHttpRequest (XHR)
XHR是浏览器提供的API,用于在后台与服务器交换数据。
基本用法
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
}
};
xhr.send();
readyState值
- 0: 未初始化(未调用open)
- 1: 已打开(已调用open)
- 2: 已发送(已调用send)
- 3: 接收中(已接收部分响应)
- 4: 完成(全部响应接收完毕)
简易封装AJAX
function ajax(options) {
const { url, method = 'GET', data = null, headers = {}, success, error } = options;
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
// 设置请求头
for (const key in headers) {
xhr.setRequestHeader(key, headers[key]);
}
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
success && success(xhr.responseText);
} else {
error && error(xhr.status, xhr.statusText);
}
}
};
xhr.onerror = function() {
error && error(xhr.status, xhr.statusText);
};
xhr.send(data);
}
// 使用示例
ajax({
url: 'https://api.example.com/data',
method: 'POST',
data: JSON.stringify({ key: 'value' }),
headers: {
'Content-Type': 'application/json'
},
success: function(response) {
console.log('Success:', response);
},
error: function(status, statusText) {
console.error('Error:', status, statusText);
}
});
Fetch API
Fetch是现代浏览器提供的更简洁的API,基于Promise。
基本用法
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
带选项的Fetch
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
body: JSON.stringify({ key: 'value' })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
响应类型及数据
响应类型
可以通过response.type获取:
- basic: 同源请求
- cors: 跨域请求
- opaque: 对非同源资源的请求,且服务器未返回CORS头
- opaqueredirect: 重定向响应
数据处理方法
response.text(): 获取文本response.json(): 获取JSON对象response.blob(): 获取Blob对象response.arrayBuffer(): 获取ArrayBufferresponse.formData(): 获取FormData对象
服务器接收数据类型
服务器通常通过Content-Type头识别客户端发送的数据类型:
-
application/x-www-form-urlencoded: 表单默认编码fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'key1=value1&key2=value2' }); -
multipart/form-data: 用于文件上传const formData = new FormData(); formData.append('file', fileInput.files[0]); formData.append('username', 'user1'); fetch(url, { method: 'POST', body: formData }); -
application/json: JSON数据fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: 'value' }) }); -
text/plain: 纯文本 -
application/xml: XML数据
服务器返回数据类型
服务器通过Content-Type响应头指定返回数据类型:
text/html: HTML文档text/plain: 纯文本application/json: JSON数据application/xml: XML数据image/jpeg,image/png: 图片application/pdf: PDF文档application/octet-stream: 二进制流数据(文件下载)
跨域资源共享 (CORS)
当请求不同源的资源时,浏览器会实施同源策略限制。CORS机制允许服务器声明哪些源可以访问资源。
简单请求
满足以下条件的请求:
- 方法为GET、HEAD或POST
- 有限的头(Accept, Accept-Language, Content-Language, Content-Type等)
- Content-Type为
application/x-www-form-urlencoded,multipart/form-data或text/plain
预检请求 (Preflight)
不满足简单请求条件的请求会先发送OPTIONS请求进行预检。
CORS相关头
Access-Control-Allow-Origin: 允许的源Access-Control-Allow-Methods: 允许的方法Access-Control-Allow-Headers: 允许的头Access-Control-Allow-Credentials: 是否允许发送凭据Access-Control-Max-Age: 预检请求缓存时间
WebSocket
WebSocket提供了全双工通信机制,适合实时应用。
const socket = new WebSocket('wss://example.com/socket');
socket.onopen = function(e) {
console.log('Connection established');
socket.send('Hello Server!');
};
socket.onmessage = function(event) {
console.log('Message from server:', event.data);
};
socket.onclose = function(event) {
if (event.wasClean) {
console.log('Connection closed cleanly');
} else {
console.log('Connection died');
}
};
socket.onerror = function(error) {
console.log('Error:', error.message);
};