Refine v5 Mantine 服务端表单验证实战:从 HttpError 到字段级错误展示
发布时间:2026/9/12 9:56:44来源:尧图网络
Refine v5 Mantine 服务端表单验证实战从 HttpError 到字段级错误展示【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine本篇指南以 Refine v5 的 Mantine UI 集成为主体围绕服务端表单验证Server-Side Form Validation这一主题展开当 dataProvider 以特定格式返回拒绝的 Promise 时MantineuseForm会自动把服务端错误回填到对应表单字段。读完本文你将掌握HttpError错误契约、真实 dataProvider 的配置写法、create/edit 页面的完整接入代码以及如何按需关闭该行为。一、什么是服务端表单验证为什么 Refine 要内置它与在浏览器里用 JavaScript 完成的客户端校验不同服务端表单验证发生在后端代码中——数据先被提交到服务器由后端完成规则校验后再决定是否入库。这是业务系统管理后台、内部工具、B2B 应用中不可或缺的一环因为客户端校验可以被绕过真正的业务约束必须由服务端强制执行。Refine 的价值在于它没有把服务端返回错误这件事留给你手动处理而是在所有useForm衍生 hook 中开箱即用地支持服务端验证。refinedev/mantine的useForm直接继承这一能力你只需要保证两件事dataProvider 在失败时按约定的格式返回错误errors字段表单组件通过getInputProps(fieldName)绑定字段。之后错误会从服务器 → dataProvider →useForm→ 具体字段自动完成传播与渲染详见 指南文档中的 Server Side Validation 一节。二、错误契约HttpError 与 ValidationErrors服务端验证能否生效取决于 dataProvider 抛出的错误是否匹配HttpError接口。该接口定义在 packages/core/src/contexts/data/types.ts 中export interface ValidationErrors { [field: string]: | string | string[] | boolean | { key: string; message: string }; } export interface HttpError extends Recordstring, any { message: string; statusCode: number; errors?: ValidationErrors; }要点message面向用户/日志的整体错误描述statusCodeHTTP 状态码通常是 400 一类的业务错误码errors可选但决定服务端验证是否生效的字段。它是一个字段名 → 错误描述的映射表key就是表单字段名支持category.id这类嵌套路径写法value 有四种形态下一节详述。一个符合契约的错误对象示例摘自 examples/server-side-form-validation-mantine/src/App.tsximport type { HttpError } from refinedev/core; const error: HttpError { message: An error occurred while updating the record., statusCode: 400, errors: { title: [Title is required.], category.id: [Category is required.], status: [Status is required.], content: { key: form.error.content, message: Content is required., }, tags: [Tags is required.], }, };errors字段是服务端验证的开关当errors存在时useForm会自动把对应字段的错误信息展示在表单中即便message存在但errors缺失也只会走全局错误通知而不会逐字段回填。三、配置 dataProvider让失败响应携带 errors 字段为了让演示可独立运行示例项目server-side-form-validation-mantine采用refinedev/simple-rest作为基础 dataProvider并覆盖create/update方法人为模拟后端返回校验失败。这是理解真实服务端接入点的最小样板App.tsxdataProvider{{ ...dataProvider(https://api.fake-rest.refine.dev), // 演示如何从 API 处理错误 update: async () { const error: HttpError { message: An error occurred while updating the record., statusCode: 400, errors: { title: [Title is required.], category.id: [Category is required.], status: [Status is required.], content: { key: form.error.content, message: Content is required., }, tags: [Tags is required.], }, }; return Promise.reject(error); }, create: async () { // 与 update 同理返回 Promise.reject(error) }, }}在真实项目中后端接口通常返回{ message, errors: { field: 错误原因 } }之类的 JSON。你需要在 dataProvider 的create/update等变更方法中捕获非 2xx 响应并将其转换或直接透传为HttpError结构后Promise.reject。参考实现位于 packages/simple-rest 与 packages/core/src/contexts/data/types.ts。四、错误值四种形态与字段级传播规则ValidationErrors中每个字段的值可以是四种类型MantineuseForm在 packages/mantine/src/hooks/form/useForm/index.ts 中通过onMutationError回调逐一处理值类型示例字段上展示的错误文案stringTitle is required.原样展示string[][Title is required.]数组合并以空格连接后展示booleantrue展示通用文案Field is not valid.{ key, message }{ key: form.error.content, message: Content is required. }优先用useTranslate按key翻译无翻译时回退到message核心处理逻辑如下摘录自 useForm/index.tsonMutationError: (error, _variables, _context) { if (disableServerSideValidation) { refineCoreProps?.onMutationError?.(error, _variables, _context); return; } const errors error?.errors; for (const key in errors) { const fieldError errors[key]; let newError ; if (Array.isArray(fieldError)) { newError fieldError.join( ); } if (typeof fieldError string) { newError fieldError; } if (typeof fieldError boolean) { newError Field is not valid.; } if (typeof fieldError object key in fieldError) { const translatedMessage translate(fieldError.key, fieldError.message); newError translatedMessage; } setFieldError(key, newError); } refineCoreProps?.onMutationError?.(error, _variables, _context); },setFieldError来自 Mantine form因此错误最终会挂载到对应的表单字段状态上字段组件只要用getInputProps(字段名)展开即可自动呈现红色错误提示。这也解释了为什么嵌套字段category.id能精准命中Select组件——getInputProps(category.id)与错误 key 使用同一套路径规则。五、完整接入Create / Edit 页面怎么写1. 创建页examples/server-side-form-validation-mantine/src/pages/posts/create.tsx 展示了创建页的标准写法import { Create, useForm, useSelect } from refinedev/mantine; import { Select, TextInput, Text, MultiSelect } from mantine/core; import MDEditor from uiw/react-md-editor; import type { ITag } from ../../interfaces; export const PostCreate: React.FC () { const { saveButtonProps, getInputProps, errors } useForm({ initialValues: { title: , status: , category: { id: }, content: , }, }); const { selectProps } useSelect({ resource: categories, pagination: { mode: server }, }); const { selectProps: tagSelectProps } useSelectITag({ resource: tags, pagination: { mode: server }, }); return ( Create saveButtonProps{saveButtonProps} form TextInput idtitle mt{8} labelTitle placeholderTitle {...getInputProps(title)} / Select idstatus mt{8} labelStatus placeholderPick one {...getInputProps(status)} data{[ { label: Published, value: published }, { label: Draft, value: draft }, { label: Rejected, value: rejected }, ]} / Select idcategoryId mt{8} labelCategory placeholderPick one {...getInputProps(category.id)} {...selectProps} / MultiSelect idtags {...getInputProps(tags)} {...tagSelectProps} mt{8} labelTags placeholderPick multiple defaultValue{[]} filter{(value, _selected, item) !!item.label?.toLowerCase().includes(value) } / Text mt{8} weight{500} sizesm color#212529 Content /Text MDEditor idcontent >import { Edit, useForm, useSelect } from refinedev/mantine; export const PostEdit: React.FC () { const { saveButtonProps, getInputProps, errors, refineCore: { query: queryResult }, } useForm({ initialValues: { title: , status: , category: { id: }, content: , tags: [], }, }); const defaultTags queryResult?.data?.data?.tags || []; const { selectProps } useSelectICategory({ resource: categories, defaultValue: queryResult?.data?.data.category.id, pagination: { mode: server }, }); const { selectProps: tagSelectProps } useSelectITag({ resource: tags, defaultValue: defaultTags, queryOptions: { enabled: defaultTags.length 0 }, pagination: { mode: server }, }); // ...表单结构与创建页一致 return ( Edit saveButtonProps{saveButtonProps} {/* TextInput / Select / MultiSelect / MDEditor与创建页相同 */} /Edit ); };编辑页多出的三个关键点从refineCore.query取出当前记录用于初始化useSelect的defaultValuetags使用defaultTags作为MultiSelect与useSelect的默认值并通过queryOptions.enabled在有数据时才发起标签查询表单字段在useForm内部通过useEffect监听query.data把服务端数据按initialValues的扁平化字段映射回填见 useForm/index.ts因此编辑页无需手动setValues。整个示例应用的路由与Refine提供者配置见 App.tsx其中resources声明了posts资源的 list/show/create/edit并开启了syncWithLocation与warnWhenUnsavedChanges。六、按需关闭服务端验证服务端验证默认开启但你可以通过两个途径关闭它disableServerSideValidation参数定义于 useForm/index.ts1. 单个表单级别useForm({ disableServerSideValidation: true, initialValues: { title: }, });2. 全局级别在Refine组件的options中统一关闭Refine options{{ disableServerSideValidation: true, }} /源码中的合并逻辑为useForm/index.tsconst { options } useRefineContext(); const disableServerSideValidation options?.disableServerSideValidation || disableServerSideValidationProp;即全局配置 OR 表单级配置任一为true都会跳过errors字段到表单字段的传播此时服务端错误只会通过onMutationError回调与全局错误通知体现。关闭后若仍需拿到原始错误可在refineCoreProps.onMutationError中自行处理。七、配套验证指南文档与交互式示例除本文给出的完整示例项目外仓库还提供了可交互的演示代码documentation/docs/guides-concepts/forms/index.md#server-side-validation- 给出了HttpError契约与六套 UIcore / React Hook Form / Ant Design / Mantine / Material UI / Chakra UI的错误传播说明documentation/docs/guides-concepts/forms/server-side-validation-mantine.tsx 是一个内置 Sandpack 的最小可运行示例用Promise.reject硬编码错误响应覆盖products资源的创建场景MantineuseForm的完整 API 说明见 useForm Hook 文档其源码与测试位于 packages/mantine/src/hooks/form/useForm。小结服务端表单验证是 Refine v5 Mantine 集成中零配置即可获得的能力dataProvider 按HttpError.errors契约返回错误refinedev/mantine的useForm在onMutationError中完成字段级错误传播配合getInputProps自动渲染提示对于MDEditor等非 Mantine 组件则用errors手动兜底。掌握这套模式后你在搭建真实管理后台时无需再为后端校验失败如何回显表单编写任何胶水代码。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网