其实 Node.js 最初的目的,就是实现一个完全可以由 JavaScript 来进行开发的服务器端,所以归根到底,它的后端能力之一就是实现一个 HTTP 服务器,这里我们来看看它。
1 搭建 HTTP 服务器
其实前面我们已经看过了一个例子,不过这里再来看一个 HTTP web 服务器的例子:
const http = require('http');
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('你好世界\n');
})
server.listen(port, () => {
console.log(`服务器运行在 http://${hostname}:${port}/`);
});
简要分析一下:
- 这里引入了 ref="http://nodejs.cn/api/http.html">http 模块:使用该模块来创建 HTTP 服务器
- 服务器被设置为在指定的 3000 端口上进行监听, 当服务器就绪时,则 listen 回调函数会被调用
- 传入的回调函数会在每次接收到请求时被执行, 每当接收到新的请求时,"http://nodejs.cn/api/http.html#http_event_request">request 事件会被调用,并提供两个对象:一个请求(http.IncomingMessage 对象)和一个响应(http.ServerResponse 对象)
- request 提供了请求的详细信息, 通过它可以访问请求头和请求的数据,response 用于构造要返回给客户端的数据;在此示例中:
res.statusCode = 200;
设置 statusCode 属性为 200,以表明响应成功;还设置了 Content-Type 响应头:
res.setHeader('Content-Type', 'text/plain')
最后结束并关闭响应,将内容作为参数添加到 end():
res.end('你好世界\n')
如上,一个简单的 HTTP 服务器已经完成了,只要运行对应代码即可启动;接下来我们可以看看它如何发送 HTTP 请求。
2 使用 Node.js 发送 HTTP 请求
2.1 执行 GET 请求
const https = require('https');
const options = {
hostname: 'nodejs.cn',
port: 443,
path: '/todos',
method: 'GET'
};
const req = https.request(options, res => {
console.log(`状态码: ${res.statusCode}`);
res.on('data', d => {
process.stdout.write(d);
});
});
req.on('error', error => {
console.error(error);
});
req.end();
2.2 执行 POST 请求
const https = require('https');
const data = JSON.stringify({
todo: '做点事情'
});
const options = {
hostname: 'nodejs.cn',
port: 443,
path: '/todos',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
};
const req = https.request(options, res => {
console.log(`状态码: ${res.statusCode}`);
res.on('data', d => {
process.stdout.write(d);
});
});
req.on('error', error => {
console.error(error);
});
req.write(data);
req.end();
2.3 PUT 和 DELETE
PUT 和 DELETE 请求使用相同的 POST 请求格式,只需更改 options.method 的值即可。
3 使用 Axios 发送 HTTP POST 请求
在 Node.js 中,有多种方式可以执行 HTTP POST 请求,具体取决于要使用的抽象级别。
使用 Node.js 执行 HTTP 请求的最简单的方式是使用 f="https://github.com/axios/axios">Axios 库(多嘴说一句,Vue.js 推荐使用的也是这个库):
const axios = require('axios');
axios
.post('http://nodejs.cn/todos', {
todo: '做点事情'
})
.then(res => {
console.log(`状态码: ${res.statusCode}`);
console.log(res);
})
.catch(error => {
console.error(error);
});
Axios 是第三方的库,所以需要通过包管理器先进行安装。
当然我们上面也使用了 Node.js 自带的模块来完成该操作,很明显要比 Axios 冗长不少,所以我个人也的确倾向于使用 Axios。
4 使用 Node.js 获取 HTTP 请求的正文数据
这是在请求正文中提取以 JSON 格式发送的数据的方式。
如果使用的是 Express,则非常简单:使用 body-parser Node.js 模块。
例如,获取此请求的正文:
const axios = require('axios');
axios.post('http://nodejs.cn/todos', {
todo: '做点事情'
});
这是对应的服务器端代码:
const bodyParser = require('body-parser');
app.use(
bodyParser.urlencoded({
extended: true
});
);
app.use(bodyParser.json());
app.post('/todos', (req, res) => {
console.log(req.body.todo);
});
如果不使用 Express 并想在普通的 Node.js 中执行此操作,则需要做多一点的工作,因为 Express 抽象(或者说简化)了很多工作。
要理解的关键是,当使用 http.createServer() 初始化 HTTP 服务器时,服务器会在获得所有 HTTP 请求头(而不是请求正文时)时调用回调。
在连接回调中传入的 request 对象是一个工作流;因此,必须监听要处理的主体内容,并且其是按数据块处理的。
首先,通过监听流的 data 事件来获取数据,然后在数据结束时调用一次工作流的 end 事件:
const server = http.createServer((req, res) => {
// 可以访问 HTTP 请求头
req.on('data', chunk => {
console.log(`可用的数据块: ${chunk}`);
});
req.on('end', () => {
//数据结束
});
});
因此,若要访问数据(假设期望接收到字符串),则必须将其放入数组中:
const server = http.createServer((req, res) => {
let data = [];
req.on('data', chunk => {
data.push(chunk);
});
req.on('end', () => {
JSON.parse(data).todo; // '做点事情'
});
});
5 Node.js http 模块
HTTP 核心模块是 Node.js 网络的关键模块。
可以使用以下代码引入:
const http = require('http');
该模块提供了一些属性、方法、以及类。
5.1 属性
(1)http.METHODS
此属性列出了所有支持的 HTTP 方法:
(2)http.STATUS_CODES
此属性列出了所有的 HTTP 状态代码及其描述:
(3)http.globalAgent
指向 Agent 对象的全局实例,该实例是 http.Agent 类的实例。
用于管理 HTTP 客户端连接的持久性和复用,它是 Node.js HTTP 网络的关键组件。
稍后会在 http.Agent 类的说明中提供更多描述。
5.2 方法
(1)http.createServer()
返回 http.Server 类的新实例。
用法:
const server = http.createServer((req, res) => {
//使用此回调处理每个单独的请求
})
(2)http.request()
发送 HTTP 请求到服务器,并创建 http.ClientRequest 类的实例。
(3)http.get()
类似于 http.request(),但会自动地设置 HTTP 方法为 GET,并自动地调用 req.end()。
5.3 类
HTTP 模块提供了 5 个类:
- http.Agent
- http.ClientRequest
- http.Server
- http.ServerResponse
- http.IncomingMessage
(1)http.Agent
Node.js 会创建 http.Agent 类的全局实例,以管理 HTTP 客户端连接的持久性和复用,这是 Node.js HTTP 网络的关键组成部分。
该对象会确保对服务器的每个请求进行排队并且单个 socket 被复用。
它还维护了一个 socket 池,是其高效性能的关键。
(2)http.ClientRequest
当 http.request() 或 http.get() 被调用时,会创建 http.ClientRequest 对象。
当响应被接收时,则会使用响应(http.IncomingMessage 实例作为参数)来调用 response 事件。
返回的响应数据可以通过以下两种方式读取:
- 可以调用 response.read() 方法
- 在 response 事件处理函数中,可以为 data 事件设置事件监听器,以便可以监听流入的数据
(3)http.Server
当使用 http.createServer() 创建新的服务器时,通常会实例化并返回此类。
拥有服务器对象后,就可以访问其方法:
- close() 停止服务器不再接受新的连接
- listen() 启动 HTTP 服务器并监听连接。
(4)http.ServerResponse
由 http.Server 创建,并作为第二个参数传给它触发的 request 事件。
通常在代码中用作 res:
const server = http.createServer((req, res) => {
//res 是一个 http.ServerResponse 对象
});
在事件处理函数中总是会调用的方法是 end(),它会关闭响应,当消息完成时则服务器可以将其发送给客户端。 必须在每个响应上调用它。
以下这些方法用于与 HTTP 消息头进行交互:
- getHeaderNames() 获取已设置的 HTTP 消息头名称的列表
- getHeaders() 获取已设置的 HTTP 消息头的副本
- setHeader('headername', value) 设置 HTTP 消息头的值
- getHeader('headername') 获取已设置的 HTTP 消息头
- removeHeader('headername') 删除已设置的 HTTP 消息头
- hasHeader('headername') 如果响应已设置该消息头,则返回 true
- headersSent() 如果消息头已被发送给客户端,则返回 true
在处理消息头之后,可以通过调用 response.writeHead()(该方法接受 statusCode 作为第一个参数,可选的状态消息和消息头对象)将它们发送给客户端。
若要在响应正文中发送数据给客户端,则使用 write(), 它会发送缓冲的数据到 HTTP 响应流。
如果消息头还未被发送,则使用 response.writeHead() 会先发送消息头,其中包含在请求中已被设置的状态码和消息,可以通过设置 statusCode 和 statusMessage 属性的值进行编辑:
response.statusCode = 500;
response.statusMessage = '内部服务器错误';
(5)http.IncomingMessage
http.IncomingMessage 对象可通过以下方式创建:
- http.Server,当监听 request 事件时
- http.ClientRequest,当监听 response 事件时
它可以用来访问响应:
- 使用 statusCode 和 statusMessage 方法来访问状态
- 使用 headers 方法或 rawHeaders 来访问消息头
- 使用 method 方法来访问 HTTP 方法
- 使用 httpVersion 方法来访问 HTTP 版本
- 使用 url 方法来访问 URL
- 使用 socket 方法来访问底层的 socket
因为 http.IncomingMessage 实现了可读流接口,因此数据可以使用流访问。
HTTP 服务器的部分其实不算非常复杂,毕竟我们需要实现的业务逻辑才是关键的。
from 知乎专栏-全栈开发从零开始 https://ift.tt/3dbsySc
via IFTTT
评论
发表评论