Nuxt Precognition
这是 nuxt-laravel-precognition 的新版本。它提供相同的功能,但不再依赖 Laravel。
它不只支持 $fetch 和 Laravel,还可以使用简单的 promise,针对任何实现了基本 Precognition 协议的后端。这些 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 Precognition 将满足您的需求!!
它支持任何验证库 (谁说 Zod??) 服务器端或客户端。您只需配置特定的 错误解析器
。
功能
- 兼容 Laravel
- 与验证库无关
- 客户端和服务器端验证
- 最佳 Typescript 支持
- 高度可定制
工作原理
一切都围绕 errorParsers
展开(用户定义函数,用于从 Error
payload 读取验证错误)
type ValidationErrors = Record<string, string | string[]>
interface ValidationErrorsData {
message: string
errors: ValidationErrors
}
type ValidationErrorParser = (error: Error) => ValidationErrorsData | undefined | null
您可以在全局(在 Nuxt Plugin
或自定义 eventHandler
中)或每个 form
实例中定义它们。
假设您正在使用 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
工厂函数创建您自己的自定义 eventHandler。
在 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
。.
基础 handler
中的任何逻辑都不会在 precognitiveRequests
期间被处理。
- 每个
event.context
还包括一个标志({ precognitive: boolean }
),指示请求是否为预知请求,具体取决于是否存在 Precognitive header。
预知协议
如果您需要在 nitro 之外定义自己的后端逻辑(AWS Lamba),请遵守以下要求列表。
- 预知请求必须具有
- 预知标头
{ '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 | 用于启用客户端(在 form.validate 和 form.submit 中)的 nuxtErrorParsers 的标志。 |
enableLaravelClientErrorParser | 布尔值 | false | 用于启用客户端(在 form.validate 和 form.submit 中)的 laravelErrorParsers 的标志。 |
enableLaravelServerErrorParser | 布尔值 | false | 用于启用客户端(在 definePrecognitiveEventHandler 中)的 laravelErrorParsers 的标志。 |
状态处理程序
与官方软件包一样,您可以全局或在实例级别为特定错误代码定义自定义处理程序
// 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 Precognition ✨
使用 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,
];
- 在需要的地方启用 Precognition 中间件
// 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