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??)伺服器端或用戶端。您只需要設定特定的 Error parsers
。
功能
- 相容 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
工廠函式建立您的 自訂 composable。
伺服器端如何?
相同的概念,建立一個 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',
},
}
},
})
這次,如果我們在 nuxt 設定檔中啟用預定義的解析器,錯誤將被轉換為 NuxtServerValidationError
並在用戶端捕獲。
// 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 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 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,
];
- 在需要的地方啟用預知中間件
// 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