Skip to main content

undici 简介 - Node.js

· 4 min read
Alan

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有:

示例:

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

返回一个promise结果

undici.stream([url, options, ]factory): Promise

  • url string | URL | UrlObject
  • options StreamOptions
    • dispatcherundici.request
    • methodundici.request
    • maxRedirectionsundici.request
  • factory Dispatcher.stream.factory 参考以下代码

undici.streamDispatcher.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