一、开始之前
Bun 最近发布带来最新的 v1 正式版,Bun 大而全的设计,并且贴近 Web API 标准,这里 Bun 目前支持了这些 Web API 标准,其中就包含了 fetch API, 并且 Node.js 在 Node.js v17.5.0 中实验性的增加了 fetch
。
二、Node.js 中的 Fetch
2.1)使用实验性 --experimental-fetch
ts
async function getData() {
const response = await fetch('https://github.com/');
const body = await response.text();
console.log(body);
}
getData()
sh
node app.js --experimental-fetch
Node.js 的 fetch 目前还处于试验阶段,但是社区已经实现了响应的功能。
2.2)使用社区实现 node-fetch
ts
import fetch from 'node-fetch';
const response = await fetch('https://github.com/');
const body = await response.text();
console.log(body);
三、其他服务端直接支持 Fetch API 的框架
服务支持 Fetch API 后,可方便的使用 fetch 函数,同时响应数据的方式也带来了影响,之间使用 Node HTTP repsonse.end 方法进行响应,显然直接使用 fetch apis 中的 Response
构造函数进行响应。
3.1)Bun 运行时
ts
Bun.serve({
fetch(req) {
return new Response("Bun!");
},
});
bun 对 fetch API 的支持最为直接 serve 的参数名就是 fetch, fetch 函数就是 request, 响应数据的时候使用 fetch API Response 即可。
3.2) Deno 运行时
ts
import { serve } from "https://deno.land/std@0.140.0/http/server.ts";
async function handler(req: Request): Promise<Response> {
const resp = await fetch("https://api.github.com/users/denoland", {
headers: {
accept: "application/json",
},
});
return new Response(resp.body, {
status: resp.status,
headers: {
"content-type": "application/json",
},
});
}
serve(handler);
Deno 与 Bun 类似在 serve 服务中接收 request 对象,然后响应 Response
实例,
3.3)Remix loader/action
ts
export async function loader() {
const users = await db.users.findMany();
const body = JSON.stringify(users);
return new Response(body, {
headers: {
"Content-Type": "application/json",
},
});
}
四、Web 标准 Fetch API 深度解读
4.1)fetch 相关 API设计:分块设计
api | 说明 |
---|---|
fetch | 核心请求函数,主打 promise 风格 |
Request | 请求对象构造函数 |
Response | 响应对象构造函数 |
Headers | 请求头构造函数 |
4.2)与 fetch 相关的 api
api | 说明 |
---|---|
AbortController | 取消控制器 |
FormData | 表单 |
URLSearchParams | 参数 |
4.3) fetch 函数的用法
4.3.1)用法一、单独传递字符串
ts
fetch(url)
4.3.2)用法二、传递第二个参数
以对象的形式配置: 请求方式
、请求头
和 请求体
等等,其中请求支持:
body 类型 | 说明 |
---|---|
字符串 | 默认即可 |
json字符串 | 与 'Content-Type': 'application/json;charset=utf-8' 头进行配合使用 |
表单 | 使用FormData 对象 配合 application/x-www-form-urlencoded |
文件上传 | 使用表单提交,自动配置 |
二进制 | Blob 或 arrayBuffer 格式上传 |
css
fetch(url, {
method: 'POST',
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
},
body: 'foo=bar&lorem=ipsum',
})
属性名 | 描述 |
---|---|
method |
HTTP 请求方法(例如,'GET'、'POST') |
headers |
包含请求头信息的对象,用于自定义请求头 |
body |
请求主体数据,可以是文本、二进制数据、表单数据等 |
mode |
请求模式,例如 'cors'、'no-cors'、'same-origin' |
cache |
缓存模式,例如 'default'、'no-store'、'reload' |
credentials |
请求凭证模式,例如 'same-origin'、'include'、'omit' |
redirect |
重定向模式,例如 'follow'、'error'、'manual' |
referrer |
请求的引用页 |
integrity |
用于验证资源完整性的哈希值 |
keepalive |
是否启用 keep-alive 功能 |
signal |
用于取消请求的信号对象,可用于中止请求 |
4.3.3)用法三、使用 Request 构造器与 fetch 函数
ts
// 创建一个简单的 GET 请求
const getRequest = new Request('your_request_url', {
method: 'GET',
headers: new Headers({
'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
}),
});
// 使用 Request 对象发送请求
fetch(getRequest)
.then(response => {
// 处理 GET 请求的响应
})
.catch(error => {
// 处理 GET 请求的错误
});
五、示例
5.1) bun 服务端 fetch + 浏览器 fetch
sh
bun create vite my-app
- bun 服务端
ts
import { serve } from 'bun';
serve({
port: 3000,
async fetch(req) {
if (req.method === 'POST') {
const bodyStream = req.body
const _body = await Bun.readableStreamToJSON(bodyStream);
const __body = {..._body, a: _body.a + 2}
// 处理 POST 请求
const body = 'Hello, POST request!' + JSON.stringify(__body);
const headers = {'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST'}
return new Response(body, { headers });
} else {
// 处理其他类型的请求
const body = 'Hello, GET request!';
const headers = { 'Content-Type': 'text/plain' };
return new Response(body, { headers });
}
}
});
注意: 配置跨域配置, bun 中 post 请求的请求体通过 Bun.readableStreamToJSON(bodyStream)
获取。
- react 客户端
ts
import { useEffect, useState } from 'react'
function App() {
const [data, setData] = useState()
const postData = async () => {
fetch('http://localhost:3000', {
method: 'POST',
mode: 'cors',
body: JSON.stringify({ a: 1, b: 2 })
}).then((response) => {
return response.json()
}).then((data) => {
setData(data)
})
}
useEffect(() => {
postData()
}, [])
return (
<>
// ...
</>
)
}
export default App
使用浏览器的快鱼的时候,配置 mode: 'cors'
属性。提示:也可以通过 vite 的代理配置跨域。
5.2) Remix 中使用 fetch
使用 bun 创建项目
sh
bun create remix fetch
tsx
import type { ActionArgs, V2_MetaFunction } from "@remix-run/node";
import { Form } from '@remix-run/react'
export async function action({ request }: ActionArgs) {
const body = await request.formData(); // 获取表单数据
const title = body.get('title')
const description = body.get('description')
return new Response(JSON.stringify({title, description})) // fetch Response 相应
}
export const meta: V2_MetaFunction = () => {
return [
{ title: "New Remix App" },
{ name: "description", content: "Welcome to Remix!" },
];
};
export default function Index() {
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
<h1>Welcome to Remix</h1>
<Form method="post">
<input type="text" name="title" />
<input type="text" name="description" />
<button type="submit">提交</button> // 默认发送 fetch 请求
</Form>
</div>
);
}
Remix 中表单提交并自动发送 fetch 请求,因为前后端统一,所以不存在跨域的情况。在 Remix action 函数中获取表单提交的数据,最后使用 fetch api 中的 Response 将请求过来的数据 stringify 化,响应给表单的提交接口,可以使用 useActionData 来获取,这里就不再说明。
小结
本文核心是讲解 服务端的运行时 fetch 支持与使用情况
,社区都在朝着 Web API 靠近,Deno/Bun/remix... Fetch API 虽然在前端有使用,但使用规模并不大,随着服务端靠近 Web API,使用会更加频繁,所以一个开发者对 Web API 标准掌握要更加的熟练。本文总结了 Fetch 相关的 API侧重于服务端的使用,并在不同的 JS 运行时和框架中实战。如果这篇文章能帮助到你,不妨点赞、转发来支持作者,感谢。