Nuxt Precognition
Nuxt Precognition 是一個 Nuxt 驗證模組,它以與後端無關的方式實現了 Precognition 協議。它支援任何後端或驗證庫,並且不與 Laravel 繫結。
目錄
要求
- Nuxt >= 3.x
- Node.js >= 18
為什麼選擇 Nuxt Precognition?
- 後端無關: 適用於任何支援 Precognition 協議的後端。
- 驗證庫無關: 使用 Zod、Yup 或任何其他驗證庫。
- 客戶端和伺服器端驗證: 兩端無縫驗證。
- 最佳 TypeScript 支援: 型別安全的表單和錯誤處理。
- 高度可定製: 插入您自己的錯誤解析器和狀態處理程式。
快速示例
interface User {
email: string
password: string
}
const form = useForm(
(): User => ({ email: '', password: '' }),
(body, headers) => $fetch('/api/login', { method: 'POST', headers, body })
)
功能
- 符合 Laravel 規範
- 驗證庫無關
- 客戶端和伺服器端驗證
- 支援 TypeScript
- 可定製的錯誤解析和狀態處理
安裝
在您的 Nuxt 應用程式中安裝模組
npx nuxi module add nuxt-precognition
工作原理
核心概念是錯誤解析器:從丟擲的錯誤中提取驗證錯誤的函式。
type ValidationErrors = Record<string, string | string[]>
interface ValidationErrorsData {
message: string
errors: ValidationErrors
}
type ValidationErrorParser = (error: Error) => ValidationErrorsData | undefined | null
定義 Zod 錯誤解析器
// app/utils/precognition.ts or shared/utils/precognition.ts
import { ZodError } from 'zod'
export const zodPrecognitionErrorParser: ValidationErrorParser = (error) => {
if (error instanceof ZodError) {
const errors: Record<string, string[]> = {}
for (const issue of error.issues) {
const key = issue.path.join('.')
if (key in errors) {
errors[key].push(issue.message)
continue
}
errors[key] = [issue.message]
}
return { errors, message: error.message }
}
return null
}
注意
對於伺服器端驗證,請將此檔案放置在shared/utils
資料夾中。
客戶端驗證
在客戶端全域性新增解析器。
// app/plugins/precognition.ts
export default defineNuxtPlugin(() => {
const { $precognition } = useNuxtApp()
$precognition.errorParsers.push(zodErrorParser)
// ..
})
在 setup 方法中使用 composable。
const UserSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
})
const form = useForm(
(): z.infer<typeof UserSchema> => ({
email: '',
password: '',
}),
(body, headers) => $fetch('/api/login', {
method: 'POST',
headers,
body,
}),
{
clientValidation(data) {
UserSchema.parse(data)
},
},
)
function login() {
form.submit()
}
function reset() {
form.reset()
document.getElementById('email')?.focus()
}
<form
@submit.prevent="login"
@reset.prevent="reset"
>
<div>
<label for="email">Email address</label>
<input
id="email"
v-model="form.email"
name="email"
type="email"
@change="form.validate('email')"
>
<span v-if="form.valid('email')">OK!!</span>
<span v-if="form.invalid('email')">{{ form.errors.email }}</span>
</div>
<div>
<label for="password">Password</label>
<input
id="password"
v-model="form.password"
name="password"
type="password"
autocomplete="current-password"
required
@change="form.validate('password')"
>
<span v-if="form.valid('password')">OK!!</span>
<span v-if="form.invalid('password')">{{ form.errors.password }}</span>
</div>
<div>
<button type="submit">Sign in</button>
<button type="reset">Reset</button>
</div>
</form>
伺服器端驗證
- 更新預設配置。
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'nuxt-precognition'
],
precognition: {
backendValidation: true,
enableNuxtClientErrorParser: true,
},
})
- 建立一個 Nitro 外掛來解析伺服器錯誤
// server/plugins/precognition.ts
import { ZodError } from 'zod'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('request', (event) => {
event.context.$precognition.errorParsers = [
zodErrorParser
]
})
})
- 以物件方式使用
definePrecognitiveEventHandler
,並在onRequest
鉤子中新增驗證。
// server/api/login.post.ts
import { z } from 'zod'
import { definePrecognitiveEventHandler, readBody } from '#imports'
const loginSchema = z.object({
email: z.string().email(),
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',
},
}
},
})
Precognition 協議
如果您需要在 Nitro 之外定義自己的後端邏輯,請遵循以下要求。
- Precognitive 請求必須具有
- Precognitive Header
{ 'Precognitive': 'true' }
- Precognitive Header
- 要驗證特定變數,每個鍵必須在 ValidateOnly Header 中指定,用逗號分隔並利用點符號
{ 'Precognition-Validate-Only': 'name,age,address.street,address.number' }
- 要驗證完整的表單,應省略 ValidateOnly Header 或將其定義為空字串。
- 成功的驗證響應必須具有
- Precognitive Header
{ 'Precognitive': 'true' }
- Precognitive Successful Header
{ 'Precognition-Success': 'true' }
- Precognitive Successful 狀態碼:
204
- Precognitive Header
- 錯誤驗證響應必須具有
- Precognitive Header
{ 'Precognitive': 'true' }
- 如果需要,Precognition-Validate-Only header
{ 'Precognition-Validate-Only': 'name,age,address.street,address.number' }
- 驗證錯誤狀態碼:
422
- 驗證錯誤和訊息將根據您的定義邏輯解析,或使用標準
errorParsers
- NuxtErrorParsers:
NuxtPrecognitiveErrorResponse
:Response & { _data: { data: ValidationErrorsData }}
- LaravelErrorParsers:
LaravelPrecognitiveErrorResponse
:Response & { _data: ValidationErrorsData }
- NuxtErrorParsers:
- Precognitive Header
配置
新增到您的 nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-precognition'],
precognition: {
backendValidation: true,
enableNuxtClientErrorParser: true,
// ...other options
}
})
選項
name | 型別 | 預設 | 描述 |
---|---|---|---|
validationTimeout | number | 1500 | 兩次預感知驗證請求之間的去抖時間(毫秒)。 |
backendValidation | boolean | false | 啟用預感知驗證的標誌。 |
validateFiles | boolean | false | 在預感知請求中啟用檔案驗證的標誌。 |
enableNuxtClientErrorParser | boolean | false | 在客戶端(在 form.validate 和 form.submit 中)啟用 nuxtErrorParsers 的標誌。 |
enableLaravelClientErrorParser | boolean | false | 在客戶端(在 form.validate 和 form.submit 中)啟用 laravelErrorParsers 的標誌。 |
enableLaravelServerErrorParser | boolean | 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')
},
}
})
Laravel 整合
如果您想使用 Laravel,則不需要 nuxt nitro 整合。
- 啟用後端驗證和錯誤解析器
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-precognition'],
precognition: {
backendValidation: true,
enableLaravelClientErrorParser: true,
}
})
- 外掛示例
新增 Sanctum token 預取並確保所有預感知請求得到正確處理。
// plugins/laravel.ts
export default defineNuxtPlugin((app) => {
const { $precognition } = useNuxtApp()
const token = useCookie('XSRF-TOKEN')
const api = $fetch.create({
baseURL: 'https://',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
onRequest: ({ options }) => {
if (token.value) {
const headers = new Headers(options.headers)
headers.set('X-XSRF-TOKEN', token.value)
options.headers = headers
}
},
onResponse: (context) => {
// ensure non false positive validations
$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 CORS 配置
確保 Precognitive header 將與 Nuxt 應用程式共享。
// config/cors.php
return [
'paths' => ['*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [env('FRONTEND_URL', 'https://: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);
});
就是這樣。Nuxt 驗證將與 Laravel 同步!!。
貢獻
# 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
許可證
MIT © sot1986