undici 是一款 HTTP/1.1 客户端, 用户HTTP请求. 相比较Node.js原生的http
类库使用起来更加友好.
本文翻译自 undici官方文档 Undici 是意大利语中的 11 的意思: HTTP/1.1 -> 11 -> Eleven -> Undici.
Node.js 刚刚发布的 v18.0.0 内置了 fetch 和 node:test 等标准模块, 其中 fetch 模块就来自 undici.
Install
npm i undici@5.0.0
Quick Start
quick-start.mjs
import { request } from 'undici';
const {
statusCode,
headers,
trailers,
body // body 是一种只读的stream
} = await request('http://localhost:3000/foo');
console.log('response received', statusCode);
console.log('headers', headers);
for await (const data of body) {
console.log('data', data); // 这里 data 是 Buffer 类型, body内容可能被分多次读取
}
console.log('trailers', trailers);
Body Mixins
body
mixins是格式化请求内容和响应内容最常用的方式, 常用mixins有:
.formData()
.json()
.text()
.blob()
示例:
body-mixins.mjs
import { request } from 'undici';
const { body } = await request("https://blog.alanwei.com/");
const text = await body.text(); // 响应内容
danger
body是一个只读流, 只能读取一次, 一旦调用mixin读取内容之后, body就不能再使用了. 如果在body上多次调用mixin, 比如 body.json(); body.text();
将会得到一个Promise reject, 异常内容为 TypeError: unusable
. 如果需要多次使用body, 或者body不是JSON、Form格式, 最佳实践是使用mixin .text()
读取文本内容后, 再手动格式化成期望的格式. 更多body mixin参考 Fetch Standard.
Common API Methods
这个章节展示最常用的几个API方法的文档, 其他API文档访问docs
undici.request([url, options]): Promise
参数
- url
string | URL | UrlObject
- options
RequestOptions
- dispatcher
Dispatcher
- 默认使用getGlobalDispatcher - method
String
- 如果传了options.body
, 默认是PUT
, 否则默认是GET
- maxRedirections
Integer
- 默认0
- dispatcher
返回一个promise结果
undici.stream([url, options, ]factory): Promise
- url
string | URL | UrlObject
- options
StreamOptions
- dispatcher 同
undici.request
- method 同
undici.request
- maxRedirections 同
undici.request
- dispatcher 同
- factory
Dispatcher.stream.factory
参考以下代码
undici.stream
是 Dispatcher.request
的快速版本, 该方法期望的facotry
参数需要返回一个可写的流stream.Writable
, 用于写入响应内容.
当用户想把响应内容写入一个可写入流(stream.Writable
)时, 借助factory
, 可以避免创建中间的stream.Readable
流, 用于改善性能.
示例1:
undici-stream.mjs
import { stream } from 'undici';
import { Writable } from "stream";
const bufs = [];
const response = await stream("https://blog.alanwei.com", {
method: "GET",
opaque: { bufs }
}, ({ statusCode, headers, opaque: { bufs } }) => {
return new Writable({
write(chunk, encoding, callback) {
bufs.push(chunk);
callback();
}
})
});
const html = Buffer.concat(bufs).toString("utf-8"); // 请求响应内容
const equals = bufs === response.opaque.bufs; // => true
示例2:
client-stream.mjs
import { Client } from 'undici';
import { Writable } from "stream";
const buffers = [];
const client = new Client("https://blog.alanwei.com");
const response = await client.stream({
path: "/me/",
method: "GET",
opaque: {
data: buffers
}
}, ({ opaque: { data } }) => {
return new Writable({
write(chunk, encoding, callback) {
buffers.push(chunk);
callback();
}
})
});
console.log(Buffer.concat(response.opaque.data).toString("utf-8")); // 打印响应内容
console.log(buffers === response.opaque.data); // => true