Refine Simple REST 数据提供器实战指南:REST API 集成、URL 约定与源码级定制
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
本文以 Refine 官方数据提供器@refinedev/simple-rest为主线,系统讲解如何在 Refine v5 项目中快速接入符合 json-server 设计约定的 REST API,覆盖安装、配置、分页/排序/过滤的 Query 序列化、HTTP 方法与自定义 Header,并结合仓库源码与单元测试揭示其底层实现原理。读完本文,你将能独立完成一个 REST 数据源的接入、调优,并在 API 不匹配时通过swizzle命令定制专属数据提供器。
Simple REST 是什么
@refinedev/simple-rest是 Refine 生态中面向标准 REST API 的数据提供器(data provider)实现。它的 URL 设计与查询参数约定建立在 json-server 的基础之上:资源名直接拼在apiUrl之后,分页、排序、过滤分别通过_start/_end、_sort/_order、field_operator等约定参数表达,并依靠响应头x-total-count返回数据总数。
该包导出一个工厂函数dataProvider(apiUrl, httpClient),完整实现了 RefineDataProvider接口中的核心方法,包括getList、getMany、getOne、create、update、deleteOne,以及custom和getApiUrl。它的定位是"开箱即用、按需定制":先用它快速跑通数据流,当后端 API 不遵循标准设计时,再基于它做局部或整体定制。
从仓库的 packages/simple-rest/package.json 可以看到,该包当前版本为6.0.1,运行时依赖仅axios与query-string,以@refinedev/core@^5.0.0作为 peer dependency,并要求 Node.js >= 20。
安装
在项目根目录执行:
npm install @refinedev/simple-rest # 或使用 pnpm pnpm add @refinedev/simple-rest安装后,数据提供器即作为dataProvider属性接入<Refine>组件。
快速开始
dataProvider工厂函数接受两个参数:
apiUrl(必填):API 的基础地址,所有请求都会拼接到该地址之下;httpClient(可选):自定义 axios 实例,用于在请求层统一处理认证、错误拦截、Token 刷新等逻辑;不传时使用包内置的默认实例。
import { Refine } from "@refinedev/core"; import dataProvider from "@refinedev/simple-rest"; import axios from "axios"; // 自定义 axios 实例(可选),可用于注入认证头、统一错误处理 const httpClient = axios.create(); const App = () => { return ( <Refine // httpClient 为可选参数,不传则使用内置实例 dataProvider={dataProvider("https://api.fake-rest.refine.dev", httpClient)} /* ... */ /> ); };注意:这里传给<Refine>的是dataProvider(...)的调用结果(一个方法对象),而不是函数本身;apiUrl需替换为你自己的后端地址。
URL 设计约定
数据提供器的每个方法都按标准 REST 风格构造 URL,下表来自官方文档 documentation/docs/data/packages/simple-rest/index.md,可直接作为后端路由设计对照:
| Method | URL | Query Parameters | Body |
|---|---|---|---|
getList | apiUrl/resource | pagination,sorters,filters | |
getOne | apiUrl/resource/id | ||
getMany | apiUrl/resource | id | |
create | apiUrl/resource | variables | |
update | apiUrl/resource/id | variables | |
deleteOne | apiUrl/resource/id | data: variables |
以apiUrl = "https://api.fake-rest.refine.dev"、资源posts为例:
getList请求GET /posts;getOne、update、deleteOne请求GET/PATCH/DELETE /posts/1;getMany请求GET /posts?id=1&id=2(ids 数组被query-string序列化为重复的id参数);create请求POST /posts,请求体携带variables;deleteOne的请求体通过 axios 配置的data字段携带variables(详见下文源码解析)。
分页、排序与过滤:Query 序列化源码解析
getList是逻辑最复杂的方法,其 URL 的构建逻辑集中在 packages/simple-rest/src/provider.ts,分页、排序、过滤最终都会落到 URL 的查询参数上。
分页:_start与_end
从源码看,getList对pagination的解构默认值为currentPage = 1、pageSize = 10、mode = "server"。当mode === "server"(默认值)时:
query._start = (currentPage - 1) * pageSize; query._end = currentPage * pageSize;即第 1 页请求?_start=0&_end=10,第 2 页请求?_start=10&_end=20。如果传pagination: { mode: "off" },则不分页,不追加这两个参数。测试 packages/simple-rest/test/getList/index.spec.ts 还专门验证了当过滤、排序、分页都为空时,请求 URL 上不会出现多余的?字符。
排序:_sort与_order
排序由 packages/simple-rest/src/utils/generateSort.ts 生成:把多个排序字段分别用逗号拼接进_sort与_order。例如对id升序、title降序排序,会得到?_sort=id,title&_order=asc,desc。当sorters为空时该函数返回undefined,getList不会追加排序参数。
过滤:操作符映射
过滤条件由 packages/simple-rest/src/utils/generateFilter.ts 与 packages/simple-rest/src/utils/mapOperator.ts 配合生成。mapOperator把 Refine 的逻辑操作符映射为 json-server 风格的后缀:
| Refine 操作符 | 序列化后的查询参数后缀 | 说明 |
|---|---|---|
eq | (无后缀) | 直接以字段名作为参数 |
ne | _ne | 不等于 |
gte | _gte | 大于等于 |
lte | _lte | 小于等于 |
contains | _like | 模糊匹配 |
| 其他操作符 | (无后缀) | 按字段名直接传值 |
例如过滤category.id = 1,最终请求为GET /posts?category.id=1;status ne "draft"则序列化为status_ne=draft。其余操作符(gt、lt、in、between、null、startswith等)在mapOperator中均返回空字符串,直接以field=value形式传递——这是"简单 REST"的取舍:只承诺最常见的操作符映射,特殊需求交给custom方法或 swizzle 定制。
generateFilter还有两个值得注意的行为:
field === "q"的过滤条件被原样保留为q参数,用于全文搜索场景;operator为or或and时会直接抛出错误,提示"不支持该操作符,可创建自定义数据提供器"。这意味着 Simple REST 默认不支持逻辑组过滤,需要该能力时应考虑定制。
总数:x-total-count响应头
getList返回的total来自响应头x-total-count(+headers["x-total-count"]强制转为数字);当该头缺失时回退为data.length。因此后端需要在响应头中返回真实总数,前端才能正确渲染分页。
custom 方法
custom方法用于调用非标准端点,其实现(packages/simple-rest/src/provider.ts)会把传入的sorters、filters、query依次序列化拼接进 URL,并根据method选择请求方式:put/post/patch携带 payload 体,delete通过 axios 的data配置携带 payload,其余情况默认走GET。
默认 HTTP 方法与自定义
每个数据提供器方法默认使用如下 HTTP 方法:
| Method | HTTP Method |
|---|---|
getList | GET |
getOne | GET |
getMany | GET |
create | POST |
update | PATCH |
deleteOne | DELETE |
注意update默认是PATCH(部分更新)而非PUT(整体替换),这符合 json-server 的设计习惯。
如果后端要求某个方法使用不同动词,无需改源码,只需在调用 hook 时通过meta.method覆盖:
import { useUpdate } from "@refinedev/core"; const { mutate } = useUpdate(); mutate({ resource: "posts", id: 1, values: { title: "New title", }, meta: { method: "put", }, });从源码看,meta.method的值被分为两类:getList/getMany/getOne限定为"get" | "delete" | "head" | "options",create/update/deleteOne限定为"post" | "put" | "patch"。也就是说,如果你为update传入meta.method: "put",请求会变为PUT /posts/1,body 携带variables。
传递自定义 Header
部分接口需要在单次请求级别附加认证或业务头,可以通过meta.headers传入,它会透传给 axios 请求配置:
import { useOne } from "@refinedev/core"; useOne({ resource: "posts", id: 1, meta: { headers: { "X-Custom-Header": "Custom header value", }, }, });headers只作用于本次调用,适合临时性的跨租户标识、调试头等场景;全局性的认证头更推荐在自定义httpClient的拦截器里统一注入。
错误处理与默认 axios 实例
Simple REST 内置了一个默认 axios 实例,实现在 packages/simple-rest/src/utils/axios.ts 中。它在响应拦截器的错误分支里把原始错误规整为 Refine 的HttpError结构:
const customError: HttpError = { ...error, message: error.response?.data?.message, statusCode: error.response?.status, };即从响应体提取message、从响应对象提取statusCode,统一 reject 出去,方便 Refine 的useShow、useForm等 hook 展示错误信息。当你的后端错误结构不同(比如 message 字段在error对象里),或需要携带 Authorization 头时,就应传入自定义httpClient:
import axios from "axios"; const httpClient = axios.create({ baseURL: "https://api.example.com", headers: { Authorization: `Bearer ${token}` }, }); httpClient.interceptors.response.use( (response) => response, (error) => { // 统一错误映射、401 跳转登录等 return Promise.reject(error); }, );用 swizzle 定制数据提供器
当后端 REST API 偏离 simple-rest 的约定(例如分页用page/limit、过滤用其他参数名)时,官方推荐用 Refine CLI 的swizzle命令把数据提供器源码"复制"到项目内自行修改:
在项目目录运行:
npm run refine swizzle从列表中选择
@refinedev/simple-rest;编辑生成在项目中的
rest-data-provider/index.ts,按需修改getList的分页参数、mapOperator的操作符映射等;将定制后的数据提供器传给
<Refine>:import { Refine } from "@refinedev/core"; import { dataProvider } from "./rest-data-provider"; const App = () => { return ( <Refine dataProvider={dataProvider("https://api.fake-rest.refine.dev")} /* ... */ /> ); };
swizzle 出来的rest-data-provider保留了provider.ts的完整结构,你可以在复制品上直接改_start/_end的拼法、替换query-string序列化逻辑,而无需修改 node_modules 中的包文件。仓库内 packages/simple-rest/src/provider.ts、packages/simple-rest/src/utils/generateFilter.ts、packages/simple-rest/src/utils/mapOperator.ts 就是最直接的定制参考模板。
测试验证:约定即契约
仓库为 Simple REST 配备了完整的 vitest 测试(packages/simple-rest/test),既是回归保障,也是行为契约文档:
- packages/simple-rest/test/getList/index.spec.ts 验证了
getList对https://api.fake-rest.refine.dev的请求:默认分页返回total为 1000;带category.id = 1过滤时返回 17 条;排序、过滤组合生效;空条件下 URL 不带?; - packages/simple-rest/test/utils/mapOperator.spec.ts 覆盖了
ne、gte、lte、contains等映射结果,并断言其他操作符(含and/or/between/null/startswith等)均返回空字符串——这进一步印证了"只承诺常见操作符"的设计边界; - 此外还有
create、update、deleteOne、getOne、custom等目录下的 mock 与 spec,展示了每个方法在api.fake-rest.refine.dev上的实际请求形态与响应解析。
小结
@refinedev/simple-rest的价值在于"标准约定 + 低成本定制":对符合 json-server 风格的后端,一个工厂函数调用即可完成全部 CRUD 接入;分页、排序、过滤的参数化全部由 provider.ts 与 utils 透明处理,meta.method/meta.headers提供按请求的灵活性;遇到不匹配的 API,swizzle命令让你把实现复制进项目自由改造。接入前,建议先对照本文的 URL 与参数约定表核对后端路由,再用 测试用例 中的请求形态作为联调基线,可以显著缩短集成周期。
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考