RxJS + Fetch: HTTP 请求的新纪元
Reach 是一个使用 RxJS 和原生 Fetch API 构建的 HTTP Client,它受到 Axios 的启发。
RxJS 用于实现类似于 Axios 中拦截器的功能,它被称作管道,是比拦截器更加强大的自定义工具。同时它也尽量使用 Web API 而不是自定义数据类型,这使得它十分的轻量。
它使用起来类似这样:
import {
RxFetch,
baseURLKey,
baseURLPipe,
retryKey,
retryPipe,
timeoutKey,
timeoutPipe,
paramKey,
dataSerialPipe,
searchParamsPipe,
} from "@tbontb/reach";
const client = new RxFetch({ [baseURLKey]: "https://httpbin.org" });
client.pipes.params
.use(baseURLPipe)
.use(searchParamsPipe)
.use(dataSerialPipe);
client.pipes.response.factory
.add(timeoutPipe)
.add(retryPipe);
client
.get("uuid")
.then((r) => r.json())
.then((v) => console.log(v));
client
.post(
"delay/1",
{ name: "user name" }, // data
{
[timeoutKey]: 3000,
[paramKey]: { id: "123" },
[retryKey]: { count: 2, delay: 1000 },
} // init
)
.then((r) => r.json())
.then((v) => console.log(v));
在这里我们创建了一个 HTTP Client,并添加了一些管道(相当于 Axios 的拦截器)。管道可以让我们对请求参数, Request 和 Response 进行操作,Reach
的所有功能都是使用管道实现的,包括 baseURL
,searchParams
,超时和重试等等。
HTTP 请求方法的函数签名和原生 Fetch API 一致,其中 post/put/patch
请求方法多了一个 data 参数。
client.get(
input: string | URL | Request,
init?: RequestInit
) : Promise<Response>
client.post(
input: string | URL | Request,
data: unknown,
init?: RequestInit
) : Promise<Response>
它的工作流程为:
管道的位置有三种,分别是 Params 管道,Request 管道和 Response 管道。
函数参数将通过 Params 管道进行处理,并通过 new Request(input, init)
构造请求;请求接着通过 Request 管道,并使用 fetch(request)
得到响应;响应经过 Response 管道处理成为最终的返回值。
每个位置还有三种管道类型,分别为普通管道,错误管道和工厂管道。
函数正常运行时,数据将流经普通管道并经由它们处理;当函数发生错误时,抛出的错误将由错误管道处理。工厂管道接收 input
和 init
并返回一个普通管道,这在需要根据参数动态构造管道时十分有用。
例如,baseURLPipe
是一个 Params 普通管道,它的实现如下:
const baseURLKey = Symbol("baseURL");
declare global {
interface RequestInit {
[baseURLKey]?: string | URL;
}
}
RxFetch.init[baseURLKey] = location.origin;
const baseURLPipe: ParamsPipe = ([input, init]) => {
if (typeof input === "string" && !URL.canParse(input))
return [new URL(input, init[baseURLKey]), init];
};
当 input 的类型为 string 且不是合法的 URL 时,管道将使用 baseURL 构造合法的 URL 返回。
retryPipe
是一个 Response 工厂管道,它使用 init 中的 timeout
属性和 RxJS 中的 retry
操作符构造一个 Response 普通管道,用于在请求出错时重试。
import { retry, RetryConfig } from "rxjs";
const retryKey = Symbol("retry");
declare global {
interface RequestInit {
[retryKey]?: number | RetryConfig;
}
}
RxFetch.init[retryKey] = { count: 2, delay: 500 };
const retryPipe: ResponseFactory = (_, init) => retry(<number>init[retryKey]);
你可能会注意到上面的代码中使用 Symbol 来定义我们的键,这是因为 RequestInit
并不是我们自定义的新数据类型,而是原生的 Web API。为了防止我们对 Web API 的拓展与将来的标准产生冲突,我们需要用这种方式定义键。
当前项目还处于起步阶段,如果你有好的想法,可以提 issue 或者 PR,项目地址在 https://gitlab.com/tbontb-iaq/reach。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。