You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SvelteKit中统一拦截表单/POST事件进行数据验证?

SvelteKit 统一拦截表单Action/POST请求做验证的方案

核心结论

hooks.server.ts的handle函数无法直接调用invalid(...)或访问actions对象——invalid是Action专属方法,仅能在Action函数的上下文里使用;handle的返回逻辑和Action完全独立,没有对应接口支持。

最优处理方案:通用Action包装器

推荐创建全局Action包装函数,所有页面的Action都通过这个包装器执行,统一在包装器里完成验证逻辑,避免重复代码。这种方式能直接利用SvelteKit的invalid机制,让页面自动保留表单数据并展示错误。

步骤1:编写通用包装工具

在src/lib/server/下创建工具文件(比如action-utils.ts):

import { invalid } from '@sveltejs/kit';
import type { Actions, Action } from '@sveltejs/kit';

// 全局统一验证逻辑,可根据需求修改
async function runUniversalValidation(request: Request) {
  const formData = await request.formData();
  
  // 示例:验证CSRF令牌
  const csrfToken = formData.get('_csrf');
  if (!csrfToken || typeof csrfToken !== 'string') {
    return {
      valid: false,
      errors: { _csrf: '无效的CSRF令牌' }
    };
  }

  // 可添加其他全局规则:比如必填字段检查、权限校验等
  return { valid: true };
}

// 单个Action的包装函数
export function wrapAction<T extends Action>(action: T): T {
  return async ({ request, locals, params }) => {
    const validation = await runUniversalValidation(request);
    if (!validation.valid) {
      return invalid(400, validation.errors);
    }
    // 验证通过后执行原Action的业务逻辑
    return action({ request, locals, params });
  } as T;
}

// 批量包装多个Action的工具(适用于多Action页面)
export function wrapActions(actions: Actions): Actions {
  return Object.fromEntries(
    Object.entries(actions).map(([key, action]) => [
      key,
      typeof action === 'function' ? wrapAction(action) : action
    ])
  );
}

步骤2:在页面Action中使用包装器

每个页面的+page.server.ts只需引入包装器,包裹自身业务逻辑即可:

// src/routes/contact/+page.server.ts
import { wrapActions } from '$lib/server/action-utils';

export const actions = wrapActions({
  default: async ({ request }) => {
    // 这里仅处理业务逻辑,全局验证已在包装器中完成
    const formData = await request.formData();
    // 示例:将数据存入数据库
    return { success: true };
  },
  // 多Action场景下,其他Action也会自动被包装
  archive: async ({ params }) => {
    // 业务逻辑...
    return { archived: params.id };
  }
});

备选方案:Handle函数拦截(不推荐)

如果一定要在hooks.server.ts中处理,可拦截POST请求做初步验证,但无法直接使用invalid,需自行构造响应后在页面Action中解析错误:

// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
  if (event.request.method === 'POST') {
    const formData = await event.request.formData();
    const csrfToken = formData.get('_csrf');
    
    if (!csrfToken || typeof csrfToken !== 'string') {
      return new Response(JSON.stringify({ errors: { _csrf: '无效的CSRF令牌' } }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' }
      });
    }
  }
  return resolve(event);
};

页面Action中处理错误:

// src/routes/contact/+page.server.ts
import { invalid } from '@sveltejs/kit';

export const actions = {
  default: async ({ request }) => {
    if (request.headers.get('content-type') === 'application/json') {
      const errorData = await request.json();
      return invalid(400, errorData.errors);
    }
    
    // 业务逻辑...
    return { success: true };
  }
};

内容的提问来源于stack exchange,提问作者Aerodynamic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 04:30:59