Nuxt 预知
这是 nuxt-laravel-precognition 的新版本。它提供相同的功能,但不再依赖于 Laravel。
它不再只支持 $fetch 和 Laravel,而是使用简单的 Promise,针对任何实现了基本预知协议的后端。这些 Promise 将接收表单 payload
和协议 Headers
。
示例
interface User = {
email: string
password: string
}
const form = useForm(
(): User => ({ email: '', password: '' }),
(body, headers) => $fetch('/api/login', { method: 'POST', headers, body })
)
此模块带有原生 Nitro 集成,但也可以与其他后端一起使用。
您是否只使用 Lambda?您可以使用 Lambda 预知!
它支持任何验证库(谁说 Zod??)服务器端或客户端。您只需要配置特定的 错误解析器
。
特性
- 兼容 Laravel
- 与验证库无关
- 客户端和服务器端验证
- 最佳 TypeScript 支持
- 高度可定制
工作原理
一切围绕 errorParsers
(用户定义的函数,用于从 Error
负载读取验证错误)展开
type ValidationErrors = Record<string, string | string[]>
interface ValidationErrorsData {
message: string
errors: ValidationErrors
}
type ValidationErrorParser = (error: Error) => ValidationErrorsData | undefined | null
您可以在全局范围内(在 Nuxt 插件
或自定义 事件处理程序
中)或每个 表单
实例中定义它们。
假设您正在使用 Zod。
只需创建一个 Nuxt 插件并定义“Zod 错误解析器”
// plugins/precognition.ts
export default defineNuxtPlugin(() => {
const { $precognition } = useNuxtApp()
$precognition.errorParsers.push(
(error) => {
if (error instanceof ZodError) {
const errors = {} as Record<string, string[]>
error.errors.forEach((e) => {
const key = e.path.join('.')
if (key in errors) {
errors[key].push(e.message)
return
}
errors[key] = [e.message]
})
return { errors, message: 'Validation error' }
}
return null
},
)
})
从现在开始,每次 useForm
捕获错误时,它都会运行我们的解析器,并捕获和分配任何验证错误。
如果要跨多个页面重用相同的选项,可以通过 useForm.create
工厂函数创建您的 自定义组合式函数。
服务器端如何处理
同样的想法,创建一个 Nitro 插件
// server/plugins/precognition.ts
import { ZodError } from 'zod'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('request', (event) => {
event.context.$precognition.errorParsers = [
(error) => {
if (error instanceof ZodError) {
const errors: Record<string, string[]> = {}
error.errors.forEach((e) => {
const key = e.path.join('.')
if (key in errors) {
errors[key].push(e.message)
return
}
errors[key] = [e.message]
})
const message = error.errors.at(0)?.message ?? 'Validation error'
return { errors, message }
}
},
]
})
})
如果您不想在每个请求上挂钩,可以通过 definePrecognitiveEventHandler.create
工厂函数创建自定义事件处理程序。
在 definePrecognitiveEventHandler
的 onRequest
处理程序中实现您的验证逻辑。
// server/api/login.post.ts
import { z } from 'zod'
import { definePrecognitiveEventHandler, readBody } from '#imports'
const loginSchema = z.object({
email: z.string().email().refine(_email => // Check for email uniqueness
true, { message: 'Email is already in use' },
),
password: z.string(),
}).refine((_data) => {
// Check for email and password match
// ...
return true
},
{ message: 'invalid credentials', path: ['email'] },
)
export default definePrecognitiveEventHandler({
async onRequest(event) {
const body = await readBody(event)
loginSchema.parse(body)
},
handler: () => {
return {
status: 200,
body: {
message: 'Success',
},
}
},
})
这次错误将转换为 NuxtServerValidationError
并由客户端捕获,如果我们在 Nuxt 配置文件中启用了预定义的解析器
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-precognitiion'],
precognition: {
backendValidation: true,
enableNuxtClientErrorParser: true,
}
})
请记住,只在 onRequest
处理程序中抛出 ValidationError
(使用 对象表示法
).
基本 处理程序
中的任何逻辑都不会在 预知请求
期间处理。
- 每个
event.context
还包含一个标志({ precognitive: boolean }
),指示请求是否是预知的,查看 预知标头 的是否存在。
预知协议
如果您需要在 Nitro 外部(AWS Lambda)定义自己的后端逻辑,请遵守以下要求列表。
- 预知请求必须具有
- 预知标头
{ 'Precognitive': 'true' }
- 预知标头
- 要验证特定变量,每个键必须在 ValidateOnly 标头中指定,用逗号分隔并利用点表示法
{ 'Precognition-Validate-Only': 'name,age,address.street,address.number' }
- 要验证完整表单,应省略 ValidateOnly 标头或将其定义为空字符串。
- 成功的验证响应必须具有
- 预知标头
{ 'Precognitive': 'true' }
- 预知成功标头
{ 'Precognition-Success': 'true' }
- 预知成功状态代码:
204
- 预知标头
- 错误验证响应必须具有
- 预知标头
{ 'Precognitive': 'true' }
- 如果需要,ValidationOnly 标头
{ 'Precognition-Validate-Only': 'name,age,address.street,address.number' }
- 验证错误状态代码:
422
- 验证错误和消息将根据您定义的逻辑进行解析,或使用标准
errorParsers
- NuxtErrorParsers:
NuxtPrecognitiveErrorResponse
:Response & { _data: { data: ValidationErrorsData }}
- LaravelErrorParsers:
LaravelPrecognitiveErrorResponse
:Response & { _data: ValidationErrorsData }
- NuxtErrorParsers:
- 预知标头
快速设置
使用一条命令将模块安装到您的 Nuxt 应用程序中
npx nuxi module add nuxt-precognition
配置
名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
validationTimeout | 数字 | 1500 | 两次预知验证请求之间的去抖时间(毫秒)。 |
backendValidation | 布尔值 | false | 启用预知验证的标志。 |
validateFiles | 布尔值 | false | 启用预知请求上文件验证的标志。 |
enableNuxtClientErrorParser | 布尔值 | false | 启用客户端 nuxtErrorParsers(在 form.validate 和 form.submit 中)的标志。 |
enableLaravelClientErrorParser | 布尔值 | false | 启用客户端 laravelErrorParsers(在 form.validate 和 form.submit 中)的标志。 |
enableLaravelServerErrorParser | 布尔值 | false | 启用客户端 laravelErrorParsers(在 definePrecognitiveEventHandler 中)的标志。 |
状态处理程序
与 官方包 中一样,您可以全局或在实例级别为特定错误代码定义自定义处理程序
// plugins/precognition.ts
export default defineNuxtPlugin(() => {
const { $precognition } = useNuxtApp()
$precognition.statusHandlers = {
401: async (error, form) => {
form.error = createError('Unauthorized')
await navigateTo('/login')
},
403: async (error, form) => {
form.error = createError('Forbidden')
},
}
})
就是这样!您现在可以在您的 Nuxt 应用程序中使用 Nuxt 预知 ✨
使用 Laravel
- 定义一个像这样的插件
// plugins/api.ts
export default defineNuxtPlugin((app) => {
const { $precognition } = useNuxtApp()
const token = useCookie('XSRF-TOKEN')
const api = $fetch.create({
baseURL: 'https://127.0.0.1',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
onRequest: ({ options }) => {
// Setup csrf protection for every requests if available
if (token.value) {
const headers = new Headers(options.headers)
headers.set('X-XSRF-TOKEN', token.value)
options.headers = headers
}
},
onResponse: (context) => {
// ensure that all precognitive requests will receive precognitive responses
$precognition.assertSuccessfulPrecognitiveResponses(context)
},
})
async function fetchSanctumToken() {
try {
await api('/sanctum/csrf-cookie')
token.value = useCookie('XSRF-TOKEN').value
if (!token.value) {
throw new Error('Failed to get CSRF token')
}
}
catch (e) {
console.error(e)
}
}
app.hook('app:mounted', fetchSanctumToken)
return {
provide: {
api,
sanctum: {
fetchToken: fetchSanctumToken,
token,
},
},
}
})
- 启用后端验证和原生 Laravel 错误解析器客户端或服务器端
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-precognition'],
precognition: {
backendValidation: true,
enableLaravelClientErrorParser: true,
},
/*
...
*/
})
* 如果您 enableLaravelServerErrorParser
,则还必须 enableNuxtClientErrorParser
- 设置 Laravel Cors 配置文件
// config/cors.php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
*/
'paths' => ['*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [env('FRONTEND_URL', 'https://127.0.0.1:3000')],
'allowed_headers' => ['*'],
'exposed_headers' => ['Precognition', 'Precognition-Success'],
'max_age' => 0,
'supports_credentials' => true,
];
- 在需要的地方启用预知中间件
// routes/api.php
Route::middleware('precognitive')->group(function () {
Route::apiResource('posts', \App\Http\Controllers\PostController::class);
});
贡献
本地开发
# Install dependencies
npm install
# Generate type stubs
npm run dev:prepare
# Develop with the playground
npm run dev
# Build the playground
npm run dev:build
# Run ESLint
npm run lint
# Run Vitest
npm run test
npm run test:watch
# Release new version
npm run release