1. HTTP #
- HTTP请求和响应模型描述的是客户端和服务器之间如何进行HTTP通信的过程。这个过程基于请求/响应模型,即客户端发起请求,服务器响应请求。
- 以下是一个简单的HTTP请求和响应模型的流程:
- 客户端发起请求:客户端(通常是浏览器,但也可以是任何可以发送HTTP请求的应用)生成一个HTTP请求消息并发送给服务器。这个请求消息包括请求行(包括HTTP方法,请求的URL,和HTTP版本),请求头(包含了如User-Agent,Accept,Cookie等一些元数据),以及可选的请求体(如POST或PUT请求中的数据)
- 服务器处理请求:服务器接收到HTTP请求消息后,解析请求行,请求头和请求体,然后进行相应的处理,这可能包括查询数据库,执行计算,或者调用其他服务
- 服务器发送响应:服务器处理完请求后,会生成一个HTTP响应消息并发送回客户端。这个响应消息包括响应行(包括HTTP版本,状态码,和原因短语),响应头(包含了如Content-Type,Set-Cookie等一些元数据),以及可选的响应体(如请求的资源,错误消息等)
- 客户端处理响应:客户端接收到HTTP响应消息后,解析响应行,响应头和响应体,然后进行相应的处理,这可能包括显示HTML页面,处理JSON数据,或者处理错误消息
1.1 请求 #
1.1.1 请求行 #
- HTTP请求行是HTTP请求的第一部分,它指定了要执行的请求类型(也称为方法),资源的标识符(URL),以及HTTP的版本
- 它通常位于HTTP请求消息的首行。以下是其格式:
<method> <request-URI> <version>
method
:这是HTTP方法,例如GET
, POST
, PUT
, DELETE
等。这些方法定义了对请求的URI指定的资源进行何种操作
GET
:请求指定的页面信息,并返回实体主体。
POST
:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
PUT
:从客户端向服务器传送的数据取代指定的文档的内容。
DELETE
:请求服务器删除指定的页面。
request-URI
:这是请求的目标资源的标识符,通常是URL。
version
:这是客户端使用的HTTP协议的版本,常见的有HTTP/1.0和HTTP/1.1
1.1.2 请求头 #
- HTTP请求头是在HTTP请求中包含的一组键值对,它们提供了有关请求或请求客户端的信息
- 这些信息可能包括客户端(如浏览器)类型、接受的响应格式、发起请求的页面的来源等等
- 以下是一些常见的HTTP请求头:
请求头 |
描述 |
Accept |
该头部告诉服务器,客户端可以处理的媒体类型,如text/html 、application/json 等。 |
Content-Type |
该头部在POST和PUT请求中使用,告诉服务器请求体的媒体类型。常见的值包括application/x-www-form-urlencoded 、multipart/form-data 和application/json 等。 |
User-Agent |
该头部包含了关于发起请求的用户代理(通常是浏览器)的信息。 |
Authorization |
该头部用于传递身份验证凭据。 |
Cookie |
该头部携带了由服务器设置的cookie,服务器可以使用它们来识别和跟踪用户。 |
Referer |
该头部指示发起请求的页面的URL,这可以帮助服务器了解哪些页面在链接到其资源。 |
X-Requested-With |
这是一个自定义的HTTP头,最常用于识别Ajax请求。例如,某些框架(如jQuery)会自动添加X-Requested-With: XMLHttpRequest 头。 |
1.1.3 请求体 #
- 请求体(Request Body)是HTTP请求中的一部分,通常用于POST和PUT方法中,用于向服务器发送要处理的数据
- 这些数据可能是用户在表单中填写的数据,也可能是JSON对象,或者其他格式的数据
- 请求体的格式取决于
Content-Type
请求头的值
- 例如,如果"Content-Type"头的值为
application/x-www-form-urlencoded
,那么请求体的数据格式应该如下:
name=zhufeng&age=18
- 如果
Content-Type
头的值为application/json
,那么请求体的数据格式应该如下:
{
"name": "zhufeng",
"age": 18
}
- 如果"Content-Type"头的值为"multipart/form-data",用于在HTTP请求中发送二进制数据,则通常用于上传文件
- 请求头指定了"Content-Type"为"multipart/form-data",并且定义了一个边界字符串"boundary"
- 请求体由几个部分组成,每个部分用边界字符串隔开
- 每个部分由一个或多个头部和一个主体组成
- "Content-Disposition"头指定了字段的名字,如果这个部分包含一个文件,它还会包含文件的名字
- "Content-Type"头是可选的,用于指定该部分主体的媒体类型
- 请求体的最后一个部分后面跟着一个带有两个连字符的边界字符串,表示请求体的结束
POST /upload HTTP/1.1
Host: localhost
Content-Type: multipart/form-data;boundary="boundary"
--boundary
Content-Disposition: form-data; name="field1"
value1
--boundary
Content-Disposition: form-data; name="field2"; filename="example.txt"
Content-Type: text/plain
Hello, World!
--boundary--
在HTTP GET请求中,通常没有请求体,因为GET请求用于获取资源,而不是发送数据。数据通常以查询参数(query parameters)的形式附加在URL中
1.2 响应 #
1.2.1 响应行 #
- 响应行(Status-Line)是HTTP响应消息的第一行,也是响应消息的一部分
- 它包含了三个部分:HTTP版本,状态码,以及原因短语
- HTTP版本:这表明了服务器使用的HTTP版本。典型的值包括 "HTTP/1.0" 和 "HTTP/1.1"。在最新的HTTP版本,HTTP/2中,这个值保持为"HTTP/1.1",因为HTTP/2的响应是以二进制格式,而不是文本格式发送的
- 状态码:这是一个三位数字的代码,它表明了请求的结果。状态码被分为几个类别,从100到500,每个类别都有特定的含义。例如,200系列的状态码表示请求已成功处理,400系列的状态码表示客户端错误,500系列的状态码表示服务器错误
- 原因短语:这是一个简短的描述,说明了状态码的含义。例如,状态码200的原因短语是 "OK",状态码404的原因短语是 "Not Found"
- 在下面这个例子中,HTTP版本是1.1,状态码是200,原因短语是 "OK"。这表明了请求已经被成功处理
HTTP/1.1 200 OK
状态码
- HTTP状态码是由三位数字组成,每个数字都有特定的含义
- 以下是HTTP状态码分类的总结:
状态码类别 |
描述 |
1xx |
信息响应:请求已收到,继续处理。 |
2xx |
成功:请求已被服务器接收、理解、并接受。 |
3xx |
重定向:需要进一步的操作以完成请求。通常这些状态码用于URI已经改变的情况。 |
4xx |
客户端错误:请求包含语法错误或无法完成请求。通常这些状态码用于服务器无法完成明显无效的请求。 |
5xx |
服务器错误:服务器在处理请求的过程中发生错误。通常这些状态码用于所有其他错误情况。 |
状态码 |
名称 |
描述 |
200 |
OK |
请求成功。这通常是对成功GET和PUT请求的响应。 |
201 |
Created |
请求成功,并创建了新的资源,通常是对POST请求的响应。 |
204 |
No Content |
请求成功,但没有要返回的表示,通常用于DELETE和PUT请求。 |
301 |
Moved Permanently |
被请求的资源已永久移动到新位置,并提供了新的URL。 |
302 |
Found |
被请求的资源临时从不同的URI响应请求。 |
400 |
Bad Request |
请求无法被服务器理解,由于客户端语法错误。 |
401 |
Unauthorized |
请求需要用户验证。 |
403 |
Forbidden |
服务器理解请求,但是拒绝执行它。 |
404 |
Not Found |
请求的资源在服务器上没有找到。 |
500 |
Internal Server Error |
服务器遇到了一个未知的错误。 |
503 |
Service Unavailable |
服务器暂时无法处理请求,通常这是因为过载或维护。 |
1.2.2 响应头 #
- HTTP响应头包含了服务器对请求的响应信息,比如资源的元数据或者关于服务器本身的信息
- 响应头可以为客户端(如浏览器)提供一些额外的上下文信息,以帮助其正确解析和处理响应
- 下面是一些常见的HTTP响应头:
响应头 |
描述 |
Content-Type |
指定了响应体的媒体类型,如 text/html 、application/json 等。 |
Content-Length |
指定了响应体的字节数。这可以让客户端知道何时读取完整的响应体。 |
Set-Cookie |
用于向客户端设置cookie。服务器可以使用cookie来识别和跟踪用户。 |
Cache-Control |
指定了缓存策略,告诉客户端是否以及如何缓存响应。 |
Location |
当服务器发送重定向响应(如 301 或 302)时,Location 头指定了重定向的位置。 |
Server |
描述了响应服务器的软件或者版本信息。 |
WWW-Authenticate |
用于401响应,指示客户端如何进行身份验证。 |
Access-Control-Allow-Origin |
用于指定哪些域可以访问资源,用于CORS(跨源资源共享)策略。 |
1.2.3 响应体 #
- 响应体(Response Body)是HTTP响应中的一部分,它包含了服务器返回的数据。这些数据可能是HTML页面、JSON对象、二进制数据(如图片或文件)等
- 响应体的格式和内容取决于请求的资源和服务器的处理。例如,当你访问一个网页时,服务器通常会返回一个HTML文档作为响应体。当你调用一个API时,服务器可能会返回一个JSON对象作为响应体
- 响应体的数据类型通常由"Content-Type"响应头指定。例如,如果响应体包含JSON数据,那么"Content-Type"头的值可能会是"application/json"
- 在某些情况下,响应可能不包含响应体。例如,对于状态码为204 ("No Content") 的响应和HEAD请求的响应,就不包含响应体
2. 请求响应演示 #
2.1 HTTP服务器 #
const http = require('http');
const url = require('url');
const fs = require('fs');
const path = require('path');
let users = [];
const server = http.createServer(async (req, res) => {
const parsedUrl = url.parse(req.url, true);
const { pathname } = parsedUrl;
const { id } = parsedUrl.query;
if (pathname === '/') {
res.end('hello world');
} else if (pathname === '/users') {
switch (req.method) {
case 'POST':
const newUser = await getRequestBody(req);
newUser.id = users.length + 1;
users.push(newUser);
res.end('用户创建成功');
break;
case 'DELETE':
if (id) {
users = users.filter(user => user.id !== parseInt(id));
res.end('用户删除成功');
}
break;
case 'PUT':
if (id) {
const updateUser = await getRequestBody(req);
users = users.map(user => user.id === parseInt(id) ? {
...user,
...updateUser
} : user);
res.end('用户更新成功');
}
break;
case 'GET':
if (id) {
const user = users.find(user => user.id === parseInt(id));
res.end(JSON.stringify(user));
} else {
res.end(JSON.stringify(users));
}
break;
default:
res.end('很抱歉,暂时不支持该请求方法');
break;
}
} else {
const ext = path.extname(pathname);
const filePath = path.join(__dirname, pathname);
if (ext === '.html' || ext === '.js' || ext === '.css') {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
res.statusCode = 404;
res.end('Not found');
} else {
res.setHeader('Content-Type', getContentType(ext));
res.end(data);
}
});
} else {
res.end('Invalid path');
}
}
});
function getContentType(ext) {
switch (ext) {
case '.html':
return 'text/html';
case '.js':
return 'text/javascript';
case '.css':
return 'text/css';
default:
return 'text/plain';
}
}
function getRequestBody(req) {
return new Promise(resolve => {
let body = '';
req.on('data', chunk => {
body += chunk;
});
req.on('end', () => {
resolve(JSON.parse(body));
});
});
}
server.listen(3000, () => console.log(`Server is listening on port 3000`));
2.2 HTTP客户端 #
2.2.1 curl #
- curl 是一个强大的命令行工具,它可以用来传输或接收数据
- -d 参数用来指定 POST 数据
- -X 参数用来指定 HTTP 方法
- -H 参数用来指定 HTTP 头
2.2.2 创建一个新用户(POST请求) #
curl -v -X POST -H "Content-Type: application/json" -d '{"name":"zhu","age":16}' http:
2.2.3 更新用户信息(PUT请求) #
curl -v -X PUT -H "Content-Type: application/json" -d '{"name":"feng","age":17}' http:
2.2.4 查询用户信息(GET请求) #
curl -v -X GET http:
2.2.5 删除用户(DELETE请求) #
curl -v -X DELETE http:
3. Cookie #
- Cookie是由网络服务器保存在用户计算机中的一小块数据,主要用于保持服务器和用户浏览器之间的状态。其功能包括存储会话信息、用户设置、购物车内容等
- 以下是Cookie的一些关键特性和用法:
- 保存状态信息:当用户浏览一个网站时,他们可能需要登录,选择某些选项,或进行其他需要保存状态的活动。Cookie可以在用户的机器上存储这些信息,以便在他们返回时能够提供相同的个性化体验
- 跟踪用户:Cookie也可以用于跟踪用户在网站上的行为,例如哪些页面被访问了,这些信息可以用于分析用户行为或定位广告
- 会话管理:对于登录网站、购物车、游戏分数或其他需要保持跨多个会话的信息,Cookies是非常有用的
- 个性化:用户可能会看到某个网站保存了他们的设置(例如用户名、语言、地点、主题等),这使得网站在下次访问时可以提供个性化的体验
- Cookie主要通过HTTP的
Set-Cookie
头部字段来设置,可以通过JavaScript的document.cookie
属性来读取和设置
3.1 工作流程 #
- 设置 Cookie:当用户第一次访问一个网站,服务器会通过 HTTP 响应头中的
Set-Cookie
字段来设置一个或多个 Cookie。一个 Set-Cookie
可能看起来像这样:Set-Cookie: id=1;
。这将在用户的浏览器中设置一个名为 "id" 的 Cookie,值为 "1"
- 发送 Cookie:一旦设置了 Cookie,浏览器将在后续的每个请求中将 Cookie 附加到 HTTP 请求头中,发送到同一服务器。这通常用于识别用户,以便服务器可以提供个性化的响应
const http = require('http');
const server = http.createServer((req, res) => {
let cookies = req.headers['cookie'];
console.log('Cookies: ', cookies);
res.setHeader('Set-Cookie', ['id=1', 'age=18']);
res.statusCode = 200;
res.end('hello');
});
server.listen(3000, () => console.log(`Server is listening on port 3000`));