@chzky/reqwest
v0.1.3
Published
[](https://jsr.io/@chzky/reqwest)
Readme
@chzky/reqwest: HTTP Request Library for chzky
@chzky/reqwest 是一个基于 Axios 的 HTTP 请求库。它采用函数式编程风格,通过不可变(immutable)的构建器模式和基于 Result 类型(来自 @chzky/core)的错误处理机制来构建和发送请求。
Features
- 基于 Axios: 底层使用
axios发送请求,继承其稳定性。 Result驱动: 所有请求的最终结果都被包装在Result<T, E>对象中,强制进行显式的错误处理,避免了try...catch块的泛滥。- 不可变 API: 所有配置方法(如
.request(),.response(),.conf())都会返回一个新的Reqwest实例,而不是修改当前实例。这使得创建可复用的基础客户端变得容易。 - 链式调用: 通过链式调用组合拦截器来构建请求。
- 内置工具函数: 提供了一系列用于 RESTful 请求、数据验证、取消请求和加载状态管理的辅助函数。
Install
pnpm install @chzky/reqwest or deno
deno add jsr:@chzky/reqwest核心概念
1. Result 结果类型
本库不通过抛出异常来处理网络或逻辑错误。dispatch() 方法返回一个 Promise<Result<SuccessType, ErrorType>>。你需要通过 @chzky/core 提供的 Result 类型来处理请求结果。
2. 不可变构建器
Reqwest 实例是不可变的。每次调用配置方法时,都会基于当前配置克隆出一个新的实例。
// baseClient 不会改变
const baseClient = Reqwest.new().conf({ baseURL: '/api' });
// userClient 是一个带有特定拦截器的新实例
const userClient = baseClient.request(get('/user'));
// postClient 也是一个独立的新实例
const postClient = baseClient.request(get('/post'));3. 拦截器 (Interceptors)
库的核心功能由拦截器驱动。拦截器是普通的函数,分为两类:
- 请求拦截器 (
RequestInterceptor): 接收AxiosRequestConfig对象,用于在请求发送前修改配置。它必须返回Ok<void>或Err<E>。 - 响应拦截器 (
ResponseInterceptor): 接收上一个拦截器或原始请求的响应数据,用于转换数据。它必须返回Ok<NewData>或Err<E>。
基本用法
下面是一个获取用户数据并提取 data 字段的基础示例。
import { Reqwest, get, pick } from '@chzky/reqwest';
import { is_ok } from '@chzky/core';
interface User {
id: number;
name: string;
}
async function fetchUser(id: number) {
// 1. 创建一个新的 Reqwest 实例
const result = await Reqwest.new()
.conf({ baseURL: 'https://jsonplaceholder.typicode.com/todos/' })
// 2. 添加一个请求拦截器来设置请求方法和 URL
.request(get(`/api/users/${id}`))
// 3. 添加一个响应拦截器来提取响应体中的 `data` 字段
.response(pick('data'))
// 4. 发送请求并等待 Result
.dispatch<User>();
if (is_ok(result)) {
const user = result.unwrap();
console.log('User data:', user);
// -> { id: 1, name: 'John Doe' }
} else {
const error = result.unwrap_err();
console.error('Failed to fetch user:', error.message);
}
}
fetchUser(1);API 参考
添加请求头
import { header } from '@chzky/reqwest';
const client = Reqwest.default()
.request(header({
'Authorization': 'Bearer token',
'X-Custom-Header': 'value'
}));设置超时
import { timeout } from '@chzky/reqwest';
const client = Reqwest.default()
.request(timeout(5000));输入验证
import { input_validate } from '@chzky/reqwest';
import { z } from 'zod';
const schema = z.object({
name: z.string().min(1),
age: z.number().min(0)
});
const client = Reqwest.default()
.request(input_validate(schema, 'data'));GET 请求
import { get, pick } from '@chzky/reqwest';
const client = Reqwest.default()
.request(get('/api/users', { page: 1, limit: 10 }))
.response(pick('data'));
const result = await client.dispatch();POST 请求
import { post } from '@chzky/reqwest';
const client = Reqwest.default()
.request(post('/api/users', { name: 'John', age: 25 }));
const result = await client.dispatch();PUT 请求
import { put } from '@chzky/reqwest';
const client = Reqwest.default()
.request(put('/api/users/1', { name: 'John Updated' }));
const result = await client.dispatch();DELETE 请求
import { del } from '@chzky/reqwest';
const client = Reqwest.default()
.request(del('/api/users/1'));
const result = await client.dispatch();表单提交
import { form } from '@chzky/reqwest';
const client = Reqwest.default()
.request(form('/api/upload', {
file: fileObject,
name: 'document'
}));
const result = await client.dispatch();输出验证
import { output_validate } from '@chzky/reqwest';
import { z } from 'zod';
const responseSchema = z.object({
id: z.number(),
name: z.string()
});
const client = Reqwest.default()
.response(output_validate(responseSchema));
const result = await client.dispatch();数据映射
import { response_map } from '@chzky/reqwest';
const client = Reqwest.default()
.response(response_map(data => ({
...data,
processed: true
})));
const result = await client.dispatch();数据提取
import { pick } from '@chzky/reqwest';
const client = Reqwest.default()
.response(pick('data'));
const result = await client.dispatch();Server-Sent Events (SSE)
import { Reqwest, parse_sse_data } from '@chzky/reqwest';
const abort = new AbortController();
const gen = Reqwest.sse({
abort: abort.signal,
url: 'https://stream.example.com/events'
});
// 使用 for await 循环
for await (const item of gen) {
if (/* 条件 */) abort.abort();
const message = parse_sse_data(item);
console.log(message.data);
}WebSocket
import { Reqwest } from '@chzky/reqwest';
const abort = new AbortController();
const ws = Reqwest.ws({
url: 'wss://echo.websocket.org',
abort: abort.signal,
deserialize: data => JSON.parse(data.data),
serialize: data => JSON.stringify(data)
});
ws.subscribe({
next: data => console.log('Received:', data),
complete: () => console.log('Connection closed')
});
ws.next({ type: 'message', data: 'Hello' });下载进度
import { download_process } from '@chzky/reqwest';
const client = Reqwest.default()
.request(download_process(progress => {
console.log(`Download progress: ${progress}%`);
}));文件下载
import { open_link, blob2url } from '@chzky/reqwest';
const [cleanup, createUrl] = blob2url();
const client = Reqwest.default()
.response(open_link('file.pdf'))
.response(createUrl);
const result = await client.dispatch();
// 使用后清理
cleanup();请求取消
import { cancel } from '@chzky/reqwest';
const [abortController, interceptor] = cancel();
const client = Reqwest.default()
.use(interceptor);
// 取消请求
abortController.abort();加载状态
import { loading_control } from '@chzky/reqwest';
const loading = ref(false);
const [controller, interceptor] = loading_control(loading, {
maximum: 10000,
maximun_handler: () => console.log('Request timeout')
});
const client = Reqwest.default()
.use(interceptor);延迟请求
import { wait } from '@chzky/reqwest';
const client = Reqwest.default()
.request(wait(1000)); // 延迟 1 秒