验证是什么

在前后端各自为政的开发模式下,前端和后端需要单独实现各自的Form表单验证逻辑。CabloyJS充分发挥全栈开发的优势,只需定义好JSON Schema配置信息,就可以同时支持前端与后端的Form表单验证逻辑。同时,还具备数据清洗特性,根据JSON Schema配置信息自动把表单字段转换成后端所需的数据类型

CabloyJS的验证机制底层采用ajv,建议您对ajv有初步的了解

定义验证信息

以模块test-party的表单formTest为例:

1. 定义Schema

配置formTestJSON Schema,定义表单字段:userNamepasswordpasswordAgainsexlanguageavatarrememberMe

JSON Schema中的字段定义主要有两个用途:

  1. 在前端可以自动渲染Form表单
  2. 在后端验证Form表单数据,并清洗Form表单数据

src/module/test-party/backend/src/config/validation/schemas.js

schemas.formTest = {
  type: 'object',
  properties: {
    userName: {
      type: 'string',
      ebType: 'text',
      ebTitle: 'Username',
      notEmpty: true,
      'x-exists': true,
    },
    password: {
      type: 'string',
      ebType: 'text',
      ebTitle: 'Password',
      ebSecure: true,
      notEmpty: true,
      minLength: 6,
    },
    passwordAgain: {
      type: 'string',
      ebType: 'text',
      ebTitle: 'Password again',
      ebSecure: true,
      notEmpty: true,
      const: { $data: '1/password' },
    },
    sex: {
      type: 'number',
      ebType: 'select',
      ebTitle: 'Sex',
      ebMultiple: false,
      ebOptions: [
        { title: 'Male', value: 1 },
        { title: 'Female', value: 2 },
      ],
      ebOptionsBlankAuto: true,
      ebParams: {
        openIn: 'page',
        closeOnSelect: true,
      },
      notEmpty: true,
    },
    language: {
      type: 'string',
      ebType: 'select',
      ebTitle: 'Language',
      ebOptionsUrl: '/a/base/base/locales',
      ebOptionsUrlParams: null,
      ebOptionsBlankAuto: true,
      'x-languages': true,
      notEmpty: true,
    },
    avatar: {
      type: 'string',
      ebType: 'file',
      ebTitle: 'Avatar',
      ebParams: { mode: 1 },
      notEmpty: true,
    },
    rememberMe: {
      type: 'boolean',
      ebType: 'toggle',
      ebTitle: 'Remember me',
    },
  },
};
名称 说明
type 字段类型,如string/number/boolean
ebType 字段渲染类型,用于标示前端渲染组件类型,如text/toggle/select
ebTitle 字段标题,用于前端渲染
notEmpty 标示此字段是否为空

2. 定义validation

src/module/test-party/backend/src/meta.js

validation: {
  validators: {
    formTest: {
      schemas: 'formTest',
    },
  },
  keywords: {
    'x-languages': keywords.languages,
  },
  schemas: {
    formTest: schemas.formTest,
  },
},
名称 说明
validation.validators 声明模块所提供的validators清单
validation.keywords 声明模块所提供的keywords清单
validation.schemas 声明模块所提供的schemas清单

validatorschema的关系

  • 一个validator可以对应多个schema,但是一般场景只需提供一个schema

后端验证

模块a-validation提供了两个验证中间件:validatevalidation

1. validate

中间件validate根据路由配置的中间件参数,自动进行Form表单验证清洗逻辑

src/module/test-party/backend/src/routes.js

{ method: 'post', path: 'kitchen-sink/form-schema-validation/saveValidation', controller: testKitchensinkFormSchemaValidation, middlewares: 'validate',
  meta: {
    validate: { validator: 'formTest' },
  },
},
名称 说明
middlewares: ‘validate’ 由于validate是局部中间件,需要显式声明
meta.validate 中间件参数
meta.validate.module 验证器所属模块,缺省为当前模块
meta.validate.validator 需要使用的验证器名称

2. validation

全局中间件validationctx.meta注入对象validation,便于直接通过代码进行验证

a-authsimple/backend/src/config/passport/auth.js

// validate
await ctx.meta.validation.validate({
  module: 'a-authsimple', validator: 'signin', data: body 
});
名称 说明
module 验证器所属模块,缺省为当前模块
validator 需要使用的验证器名称
data 需要验证的Form表单数据

前端渲染

模块a-components提供了一个全局vue组件eb-validate,主要有两个用途:

  1. 进行Form表单渲染
  2. 拦截后端返回的验证错误信息,并进行显示

1. 自动渲染

eb-validate根据validator提供的schema信息自动渲染Form表单

src/module/test-party/front/src/kitchen-sink/pages/form-schema-validation/formSchemaAuto.vue

<eb-validate v-if="item" ref="validate" auto :data="item" :params="validateParams" :onPerform="onPerformValidate" @submit.prevent="onFormSubmit">
</eb-validate>
名称 说明
auto true:自动渲染 false:自定义渲染
data Form表单数据
params 自动布局参数
params.module 验证器所属模块,默认为当前模块
params.validator 验证器名称
onPerform 在这里调用后端API接口
submit.prevent 响应Submit事件
  1. 当点击按钮Save或者响应Submit事件时,执行eb-validate的方法perform,从而触发eb-validateonPerform
  2. onPerform中调用后端API接口,如果有验证错误,就会自动显示出来

2. 自定义渲染

eb-validate内部自定义Form组件布局,这样可以有更强的针对性和灵活性

src/module/test-party/front/src/kitchen-sink/pages/form-schema-validation/formSchemaCustom.vue

<eb-validate v-if="item" ref="validate" :auto="false" :data="item" :params="validateParams" :onPerform="onPerformValidate">
  <eb-list form no-hairlines-md @submit.prevent="onFormSubmit">
    <eb-list-item-validate dataKey="userName"></eb-list-item-validate>
    <eb-list-item-validate dataKey="password"></eb-list-item-validate>
    <eb-list-item-validate dataKey="passwordAgain"></eb-list-item-validate>
    <eb-list-item-validate dataKey="sex"></eb-list-item-validate>
    <eb-list-item-validate dataKey="language"></eb-list-item-validate>
    <eb-list-item-validate dataKey="avatar"></eb-list-item-validate>
    <f7-list-item v-if="item.avatar">
      <img class="avatar avatar48" :src="getAvatarUrl(item.avatar,48)">
    </f7-list-item>
    <eb-list-item-validate dataKey="rememberMe"></eb-list-item-validate>
  </eb-list>
</eb-validate>