创建HTTP连接
Node擅长处理I/O操作,所以它不仅合适提供HTTP服务,也适合使用这些服务。接下来你将学习使用http
模块和第三方模块执行和控制http请求。
在HTTP协议中,有两个重要的属性:URL和方法。最常见的是GET、POST方法,还有PUT等别的方法。
一、创建GET请求
const http = require('http');
const options = {
host: 'localhost',
port: 1234,
path: '/'
}
http.get(options, (res) => {
console.log(res);
})
在上面这个例子中,使用TCP端口1234向主机名localhost
和路径/
执行GET请求(localhost:1234/
)。回调函数会在服务器响应到达的时候处理响应对象。
二、使用其他HTTP动词
2.1 发送POST请求
http.get 是通用的 http.request 的快捷方式,其选项如下:
- host——请求目标的主机名或者IP地址
- port——远程服务器的TCP端口号
- method——指定HTTP请求方法的字符串,值就是GET/POST/PUT/PATCH/DELETE等关键词。
- path——请求的路径,可以包含一个查询字符串和一个分隔符:/index?id=1
- headers——配置请求头
http.request 方法会返回一个 http.ClientRequest 对象,它是一个可写流,可以使用这个流发送数据,所发送的数据是请求主体数据的一部分,当完成向请求主体中写入数据时,要结束数据流来终止请求。
完整的请求,你可以这么写:
const http = require('http');
const options = {
host: 'localhost',
port: 1234,
path: '/',
method: 'POST'
}
// 发送请求,监听响应
const req = http.request(options, (response) => {
console.log(response.statusCode);
console.log(response.headers);
response.setEncoding('utf-8');
response.on('data', (chunk) => {
console.log('BODY : ' + chunk);
})
})
req.write('This is a request');
req.end();
http.request 返回ClientRequest对象,可以通过req.write()
方法将请求主体写入该对象,可以传入一个经过编码的字符串,或者缓冲区。必须使用end()
去结束请求,不然会被认为请求不完整,从而不会处理该请求。
如果服务器正常工作,会返回一个响应,客户端接收到响应触发response
事件:
req.on('response', (res) => {
console.log(res);
})
2.2 查看响应对象
响应对象是ClientResponse的一个实例,主要属性有:
- statusCode——HTTP状态码
- httpVersion——HTTP版本号
- headers——响应头
2.3 获取响应主体
响应主体并不会在请求的response事件发生时出现,而是需要监听data事件:
http.request(options, (response) => {
response.setEncoding('utf8');
response.on('data', (data) => {
console.log(data);
})
})
响应主题可以是一个缓冲区,但是如果指定了编码格式,那么就是一个编码字符串了。
2.4 以流的方式传送响应主体
HTTP响应是一个可读流,表示响应主体数据流。可以将一个可读流送入一个可写流中,例如一个HTTP请求或者一个文件:
const http = require('http');
const fs = require('fs');
const options = {
host: 'localhost',
port: 1234,
path: '/',
method: "GET"
}
const file = fs.createWriteStream('./test.text');
http.request(options, (res) => {
res.pipe(file);
}).end();
上面创建了一个可写文件流,并将响应体写入文件。
当响应到达,文件流打开,响应体写入;当响应主体结束的时候,文件流也结束,文件流被关闭。
三、使用 HTTP.Agent 维护套接字
在创建HTTP请求的时候,Node在内部使用了一个代理。
代理是Node中的一种实体,该实体用来给你创建请求,它负责维护一个套接字池,对指定的主机名和端口对,套接字池中包含已经被打开但是未被使用的连接。
当出现新的HTTP请求时,代理要求连接保持活跃状态;当请求结束,并且在套接字上没有额外的请求等待释放时,套接字就会被关闭,而不用手动关闭。
当创建了一个请求并选择了一个可用的套接字,或者为该请求创建了一个新的连接时,http.ClientRequest 会发射socket事件。在请求结束之后,套接字会在发射close事件或agentRemove事件时从套接字池中被删除。
四、应用第三方请求模块简化HTTP请求
Node的HTTP客户端非常强大,但是用起来很麻烦:
- 必须提供一个配置对象,需要所有选项,包括被分割的URL。
- 如果希望处理响应体,需要专门获取响应体。
- 如果是重定向响应,需要人工处理该响应。
通过使用 request 模块可以简化操作。
1. 安装和应用 request 模块
使用npm在当前目录下安装该模块
npm install request
然后使用:
const r = require('request');
之后只需要提供URL和回调就可以创建请求了
request('localhost:1234', (error,res,body) => {
...
})
如果按上述代码发送请求,默认为GET请求吗,如果想要使用其他HTTP动词,例如post:request.put(url);
还可以用option
对象来替代URL,可选参数可以查看官方文档。
2. 创建测试服务器
- 服务器端代码
const http = require('http');
const server = http.createServer();
// 监听请求
server.on("request", (req, res) => {
// 回送一个消息
const printBack = function () {
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end(JSON.stringify({
URL: req.url,
method: req.method,
Headers: req.headers
}))
}
// 路由
switch (req.url) {
// 重定向
case '/redirect':
res.writeHead(301, { "Location": '/' })
res.end();
break;
// 打印请求体
case '/print/body':
req.setEncoding('utf8');
let body = "";
req.on('data', (data) => {
body += data;
});
// 结束之后,响应以发送body结束
req.on('end', () => {
res.end(JSON.stringify(body));
})
break;
// 默认
default:
printBack();
break;
}
})
server.listen(1234);
- 客户端代码
const request = require('request');
const inspect = require('util').inspect;
request.get('http://localhost:1234/print/body', (err, res, body) => {
if(err) {
throw err;
}
console.log(inspect({
err: err,
res: {
statusCode: res.statusCode
},
body: JSON.parse(body)
}))
})
3. 跟随重定向
request 的特性之一是它在默认情况下能够跟随重定向。可以用上面的代码来观察这个特征:
当请求路径为/redirect
时,可以用301重定向响应码做出响应。可以用客户端来查看跟随上述URL的请求:
将请求路径修改为/redirect
,可以在客户端看到返回信息中路径为/
而不是/redirect
。
如果你希望发生重定向的时候得到通知,可以把请求选项中的followRedirect
设置为 false 。
4. 使用option
const options = {
url: 'http://localhost:1234',
method: 'POST',
headers: {
'...': '...'
}
}
request(options, function(err, res, body){
...........
})
5. 对请求体进行编码
有时候需要在请求主体上发送一些数据,可以使用表单编码对请求体进行编码:
const body = {
a: 1,
b: 2
};
const options = {
url: '...',
form: body
// 还可以采用json编码,如下:
// json: body
}
6. 流式传送
请求返回的是一个客户端请求对象,可以将该对象传入可写流当中,甚至可以将请求对象传入另一个请求。
- 传入文件
request.get('...').pipe(fs.createWriteStream('...'));
- 传入另一个请求
request.get('...').pipe(request.post('...'));