介紹一個請求庫 — Undici
theme: juejin highlight: atom-one-dark
前言
在瀏覽器中,如果想發起一個請求,我們以前會使用到 xhr
,不過這種底層 api,往往呼叫方式比較簡陋。為了提高開發效率, jQuery 的 $.ajax
可能是最好的選擇,好在後來出現了更加現代化的 fetch
api 。
但是考慮到 fetch
的相容性,而且它也不支援一些全域性性的配置,以及請求中斷,在實際的使用過程中,我們可能會用到 axios
請求庫,來進行一些請求。到了 Node.js 中,幾乎都會通過 request
這個庫,來進行請求。遺憾的是,request
在兩年前就停止維護了,在 Node.js 中需要找到一個能夠替代的庫還挺不容易的。
在 request 的 issues 中,有一個表格推薦了一些在 Node.js 中常用的請求庫:
| 包名 | 包大小 | API風格 | 簡介 | | ------------------------------------------------------ | ------ | ------------------ | ------------------------------------------------------------ | | node-fetch | 0.4kb | promise / stream | A light-weight module that brings window.fetch to Node.js | | got | 48.4kb | promise / stream | Simplified HTTP requests | | axios | 11.9kb | promise / stream | Promise based HTTP client for the browser and node.js | | superagent | 18kb | chaining / promise | Small progressive client-side HTTP request library, and Node.js module with the same API, sporting many high-level HTTP client features | | urllib | 816kb | callback / promise | Help in opening URLs (mostly HTTP) in a complex world — basic and digest authentication, redirections, cookies and more. |
瀏覽器中使用比較多的 axios
,在 Node.js 中並不好用,特別是要進行檔案上傳的時候,會有很多意想不到的問題。
最近我在網上🏄🏿的時候,發現 Node.js 官方是有一個請求庫的:undici
,名字取得還挺複雜的。所以,今天的文章就來介紹一下 undici
。順便提一句,undici
是義大利語 11
的意思,好像雙十一也快到了,利好茅臺🤔。
Undici means eleven in Italian. 1.1 -> 11 -> Eleven -> Undici. It is also a Stranger Things reference.
上手
我們可以直接通過 npm
來安裝 undici
:
bash
npm install undici -S
undici
對外暴露一個物件,該物件下面提供了幾個 API:
undici.fetch
:發起一個請求,和瀏覽器中的fetch
方法一致;undici.request
:發起一個請求,和request
庫有點類似,該方法支援 Promise;undici.stream
:處理檔案流,可以用來進行檔案的下載;
undici.fetch
注意:該方法需要 node 版本 >= v16.5.0
在通過 undici.fetch
請求服務之前,需要先通過 koa
啟動一個簡單登入服務。
```js const Koa = require('koa') const bodyParser = require('koa-bodyparser')
const app = new Koa()
app.use(bodyParser()) app.use(ctx => { const { url, method, body } = ctx.request if (url === '/login') { if (method === 'POST') { if (body.account === 'shenfq' && body.password === '123456') { ctx.body = JSON.stringify({ name: 'shenfq', mobile: '130xxxxxx' }) return } } } ctx.status = 404 ctx.body = JSON.stringify({}) })
app.listen(3100) ```
上面程式碼很簡單,只支援接受一個 POST
方法到 /login
路由。下面使用 undici.fetch
發起一個 POST
請求。
```js const { fetch } = require('undici')
const bootstrap = async () => { const api = 'http://localhost:3100/login' const rsp = await fetch(api, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ account: 'shenfq', password: '123456' }) }) if (rsp.status !== 200) { console.log(rsp.status, '請求失敗') return } const json = await rsp.json() console.log(rsp.status, json) }
bootstrap() ```
如果將請求的方式改為 GET
,就會返回 404。
js
const rsp = await fetch(api, {
method: 'GET'
})
undici.request
undici.request
的呼叫方式與 undici.fetch
類似,傳參形式也差不多。
```js const { request } = require('undici')
const bootstrap = async () => { const api = 'http://localhost:3100/login' const { body, statusCode } = await request(api, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ account: 'shenfq', password: '123456' }) }) const json = await body.json() console.log(statusCode, json) }
bootstrap() ```
只是返回結果有點不一樣,request
方法返回的 http 響應結果在 body
屬性中,而且該屬性也支援同 fetch
類似的 .json()
/.text()
等方法。
中斷請求
安裝 abort-controller
庫,然後例項化 abort-controller
,將中斷訊號傳入 request 配置中。
npm i abort-controller
```js const undici = require('undici') const AbortController = require('abort-controller')
// 例項化 abort-controller const abortController = new AbortController() undici.request('http://127.0.0.1:3100', { method: 'GET', // 傳入中斷訊號量 signal: abortController.signal, }).then(({ statusCode, body }) => { body.on('data', (data) => { console.log(statusCode, data.toString()) }) }) ```
我們執行程式碼,發現是可以請求成功的,是因為我們沒有主動呼叫中斷方法。
```js undici.request('http://127.0.0.1:3100', { method: 'GET', signal: abortController.signal, }).then(({ statusCode, body }) => { console.log('請求成功') body.on('data', (data) => { console.log(statusCode, data.toString()) }) }).catch(error => { // 捕獲由於中斷觸發的錯誤 console.log('error', error.name) })
// 呼叫中斷 abortController.abort() ```
現在執行程式碼會發現,並沒有輸出 請求成功
的日誌,進入了 catch
邏輯,成功的進行了請求的中斷。
undici.steam
undici.steam
方法可以用來進行檔案下載,或者介面代理。
檔案下載
js
const fs = require('fs')const { stream } = require('undici')const out = fs.createWriteStream('./宋代-哥窯-金絲鐵線.jpg')const url = 'http://img.dpm.org.cn/Uploads/Picture/dc/cegift/cegift6389.jpg'stream(url, { opaque: out }, ({ opaque }) => opaque)
介面代理
js
const http = require('http')const undici = require('undici')// 將 3100 埠的請求,代理到 80 埠const client = new undici.Client('http://localhost')http.createServer((req, res) => { const { url, method } = req client.stream( { method, path: url,opaque: res }, ({ opaque }) => opaque )}).listen(3100)
總結
本文只是介紹了 undici
幾個 api 的使用方式,看起來 undici
上手難道還是比較低的。但是相容性還不太行,比如,fetch
只支援 [email protected]
以上的版本。
對於這種比較新的庫,個人還是建議多觀望一段時間,雖然 request
已經廢棄了,我們還是使用一些經過較長時間考驗過的庫,比如,egg 框架中使用的 urllib,還有一個 node-fetch,上手難道也比較低,與瀏覽器中的 fetch
api 使用方式一致。
- 詳解 Webpack devtools
- 什麼是 LFU 演算法?
- 什麼是 LFU 演算法?
- 關於 Promise 的執行順序
- 關於 Promise 的執行順序
- 新一代的編譯工具 SWC
- 介紹一個請求庫 — Undici
- 你給開源專案提過 PR 嗎?
- Go 語言的模組化
- MobX 上手指南
- 介紹兩種 CSS 方法論
- 普通打工人的2020丨掘金年度徵文
- Node.js 服務效能翻倍的祕密(二)
- Node.js 服務效能翻倍的祕密(一)
- 我是如何閱讀原始碼的
- Vue3 Teleport 元件的實踐及原理
- Vue3 模板編譯優化
- 小程式依賴分析實踐
- React 架構的演變 - Hooks 的實現
- Vue 3 的組合 API 如何請求資料?