前言
前文介绍了 http
模块的基本用法,主要就是调用 createServer
和 listen
方法来创建和启动服务。要处理具体的 HTTP 请求,就要在 createServer
方法中写点什么。本文来介绍处理请求的两个核心对象之一的 request
。
URL
HTTP 协议最早设计出来,仅仅为了获取网络上的某个 HTML 文档。随着后来的发展,网络上的资源越来越丰富,种类也从单一的文本发展到图片,音频,视频等多媒体类型。但是无论是何种资源,它都有在整个互联网世界中的唯一标识,这就是 URL —统一资源定位符(Uniform Resource Locator)。
下面是 URL 的组成:
- scheme :就是我们常说的“协议”,在《HTTP 权威指南》中翻译为“方案”。常用的协议有 HTTP,FTP,SMTP 等等。
- user:password@:表示访问此资源的用户和密码。对于公开的网络资源并不需要验证。即使需要也不会这么用,很不安全。通常直接省略。
- host:port:表示资源所在的主机名和端口号。HTTP 协议的默认端口号是 80,HTTPS 协议的默认端口号是 443,都还可以省略。
- path:路径,表示资源在主机中的位置。
- query:查询,表示对资源附加的额外要求。
- #fragment:片段标识符,表示资源内部的一个锚点,浏览器可以跳转到它指示的位置。但是此部分不会发送到服务器。
request 对象
HTTP 协议是基于请求-响应模型的协议,在 HTTP2 以前,必须是客户端发起请求,服务器接收请求,再将处理的结果响应给客户端。
客户端在向服务器发送 HTTP 请求时,需要指定一个具体的 URL 和 HTTP 方法,告诉服务器我想要写什么,还是提交些什么。通常还会携带一些信息,这些信息可以放在 URL 中,或者放在 HTTP Header(首部)中,如果是向服务器提交数据,还可以放在 HTTP 报文实体中。
那么服务器就要想办法接收这些东西,http
模块中的 request
对象中就包含了这些东西,可以根据自己的需要有选择的解析出来使用。
解析 Method
HTTP 方法常用的有 Get
,Head
,Post
,Put
,Delete
等等,可以直接使用 request.method
来获取。
const server = http.createServer((request, response) => {
const method = request.method;
console.log(method)
response.end('Hello, World');
});
浏览器刷新一下页面,服务端打印:
解析 URL
URL 构成比较复杂,通过 request.url
只能获取完整的 URL。比如浏览器访问 http://localhost:3000/list?page=10
:
const server = http.createServer((request, response) => {
console.log(request.method)
console.log(request.url)
response.end('Hello, World');
});
控制台打印:
若要获得某个部分,比如路径 path,查询参数 query,可以借助内置的 url
模块:
const http = require('http');
const url = require('url');
const server = http.createServer((request, response) => {
const urlObj = url.parse(request.url)
console.log(urlObj)
response.end('Hello, World');
});
parse
方法,将原始的 URL 解析为一个对象:
解析 Query
通过上面的方式,可以得到当前请求的资源的路径和查询参数。但是查询参数依然是一个字符串,不方便使用。
可以使用内置的 querystring
模块去解析,或者使用 URLSearchParams
API 去解析,或者使用第三方的 qs
、querystringify
等模块去解析。以内置模块 querystring
为例:
const http = require('http');
const url = require('url');
const qs = require('querystring');
const server = http.createServer((request, response) => {
let { query } = url.parse(request.url)
query = qs.parse(query)
console.log(query)
response.end('Hello, World');
});
就可以轻松得到一个 query 对象,方便读取查询参数了:
解析 Header
HTTP 请求的首部信息都放在了 request.headers
对象中。
const server = http.createServer((request, response) => {
console.log(request.headers);
response.end('Hello, World');
});
浏览器发一次请求,服务端会打印:
如图,像是 sec-ch-ua
、sec-ch-ua-mobile
这种以 sec-
为前缀的请求头,表示的是禁止使用代码修改的 HTTP 消息头,用户代理保留全部对它们的控制权。比如我们熟悉的 User-Agent
,通常后端会根据它来判断用户的系统和平台,但是它很容易就被修改进行伪装,因此是不安全的。通过 sec-ch-ua
就能安全的将用户代理信息传给服务器。
除了这些安全的Header,剩下的我们都非常熟悉了:
-
host:此次请求到主机名。用于虚拟主机设置。
-
connection:控制网络连接的断开。HTTP/1.1 默认为
keep-alive
,表示长连接 -
cache-control:设置强缓存。
-
accept:客户端可接收的内容的类型。
-
accept-encoding:客户端可用的内容编码方式——通常是某种压缩算法。
若要获取具体某个 Header 的信息,可以使用方括号语法:
const server = http.createServer((request, response) => {
console.log(request.headers['content-type']);
response.end('Hello, World');
});
解析请求体
HTTP 报文分为请求报文和响应报文。报文由这三部分构成:
- 起始行:分为请求行,状态行。
- 首部:描述请求相关的信息,也会描述实体数据的信息。
- 实体:携带的数据。
有的请求,比如文件上传,表单提交,要携带一些数据,这些数据就是放在报文的实体中传输的。
HTTP 请求只管发送数据,而不管数据是何类型。所以会在请求头中通过 Content-Type
来告知服务器,此次请求所携带的数据是什么格式的。request
对象将接收到的实体中的数据,都放在 requst.body
中。根据这两点,就可以解析出客户端所传的数据。
另外,request
对象是一个可读流,通过监听 data
事件,就能读取出来实体中所传输的数据。当监听到 end
事件,表示实体中数据读取完毕,就可以进行解析了。
const http = require('http');
const url = require('url');
const server = http.createServer((request, response) => {
const method = request.method;
const { pathname } = url.parse(request.url);
// 处理 Post /user/login
if(method === 'POST' && pathname === '/user/login') {
let arr = [];
const contentType = request.headers['content-type']
if (contentType === 'application/json') {
// 监听 data 事件,读取实体数据
request.on('data', (data) => {
console.log(data)
arr.push(data)
})
// 监听 end 事件,处理数据
request.on('end', () => {
console.log('传输完毕')
let json = JSON.parse(Buffer.concat(arr).toString())
console.log(json)
})
// 结束响应
response.end('Hello, World');
}
}
});
server.listen(3000, () => {
console.log('服务器启动成功:http://localhost:3000')
})
以一个用户登录的表单提交为例,通常会使用 json
格式传递数据。打开 Postman 或者 ApiPost 来发送请求:
服务端程序打印结果如下:
总结
本文介绍了通过 request
对象几个常用到的属性来处理 HTTP 请求:
- request.method:获取请求方法
- request.url:获取请求 URL,还需要再借助
url
、querystring
等工具进一步解析 - request.headers:获取请求头信息
- request.body:获取请求体数据,需要根据
Content-Type
解析为不同的格式
下篇文章会介绍 reponse
对象,来完成完成的一次请求处理。