Appearance
前后端参数统一校验
提示
本功能需要结合acuity-web-pro + acuity-validator-starter + acuity-cloud或acuity-boot 一起使用。
我们这里以 新增岗位 为例,大致流程如下:
- 后端按照 hibernate validator 规范编写controller 和 entity,并标记相应的验证注解
- 前端进入新增页面时,就立即加载该界面的新增接口校验规则,并将后端的规则通过规则转换器formValidateService.ts 转换成前端校验组件认可的规则。
注意
formValidateService.ts 插件位于acuity-web-plus项目, 企业商用版才有。敬请见谅。**
步骤:
- 在server模块引入依赖:
xml
<dependency>
<groupId>top.acuity.commons</groupId>
<artifactId>acuity-validator-starter</artifactId>
</dependency>
<dependency>
<groupId>top.acuity.commons</groupId>
<artifactId>acuity-validator-starter</artifactId>
</dependency>
- 在 Org 类上加上 hibernate 的验证注解:
java
import org.hibernate.validator.constraints.Length;
public class Position implements Serializable {
private static final long serialVersionUID = 1L;
@Length(max = 255, message = "名称长度不能超过255")
@NotEmpty(message = "名称不能为空")
private String name;
@Length(max = 255, message = "简称长度不能超过255")
private String abbreviation;
@NotEmpty(message = "民族不能为空")
@Length(max = 2, message = "民族长度不能超过2")
private String nation;
}
import org.hibernate.validator.constraints.Length;
public class Position implements Serializable {
private static final long serialVersionUID = 1L;
@Length(max = 255, message = "名称长度不能超过255")
@NotEmpty(message = "名称不能为空")
private String name;
@Length(max = 255, message = "简称长度不能超过255")
private String abbreviation;
@NotEmpty(message = "民族不能为空")
@Length(max = 2, message = "民族长度不能超过2")
private String nation;
}
- 在controller中需要校验的方法加上
@Validated
注解:
java
@PostMapping("/position")
public R<Position> save(@RequestBody @Validated Position data) {
return success(data);
}
@PostMapping("/position")
public R<Position> save(@RequestBody @Validated Position data) {
return success(data);
}
写好 controller 方法后, 假设该方法的访问地址是
POST http://127.0.0.1:8760/api/base/position
POST http://127.0.0.1:8760/api/base/position
前端在进入新增页面时,会立即调用 http://127.0.0.1:8760/api/base/form/validator/position 接口, 获取保存组织接口的校验规则。
- 前端新增页面的代码如下
typescript
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
// 省略 处理参数回显逻辑 ...
if (unref(type) !== ActionEnum.VIEW) {
// 根据type值,获取接口
let validateApi = Api[VALIDATE_API[unref(type)]];
// 调用 getValidateRules 将请求后台获取参数验证规则
await getValidateRules(validateApi, customFormSchemaRules(type)).then(async (rules) => {
// 后台返回规则后,动态更新前端的验证规则
rules && rules.length > 0 && (await updateSchema(rules));
});
}
});
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
// 省略 处理参数回显逻辑 ...
if (unref(type) !== ActionEnum.VIEW) {
// 根据type值,获取接口
let validateApi = Api[VALIDATE_API[unref(type)]];
// 调用 getValidateRules 将请求后台获取参数验证规则
await getValidateRules(validateApi, customFormSchemaRules(type)).then(async (rules) => {
// 后台返回规则后,动态更新前端的验证规则
rules && rules.length > 0 && (await updateSchema(rules));
});
}
});
后端实现
提示
/api/base/form/validator/position 接口实际上是请求到了 FormValidatorController 类
java
@RequestMapping
public class FormValidatorController {
@RequestMapping("/form/validator/**")
@ResponseBody
public R<Collection<FieldValidatorDesc>> standardByPathVar(HttpServletRequest request) throws Exception {
String requestUri = request.getRequestURI();
String formPath = StrUtil.subAfter(requestUri, FORM_VALIDATOR_URL, false);
return R.success(localFieldValidatorDescribe(request, formPath));
}
}
@RequestMapping
public class FormValidatorController {
@RequestMapping("/form/validator/**")
@ResponseBody
public R<Collection<FieldValidatorDesc>> standardByPathVar(HttpServletRequest request) throws Exception {
String requestUri = request.getRequestURI();
String formPath = StrUtil.subAfter(requestUri, FORM_VALIDATOR_URL, false);
return R.success(localFieldValidatorDescribe(request, formPath));
}
}
前端实现
提示
getValidateRules方法位于前端项目的 formValidateService.ts 模块, 该模块适用于 async-validator 验证框架, 其他验证框架需要自行调整。
typescript
# formValidateService.ts
/**
* 从后端获取某个接口基于 Hibernate Validator 注解生成的参数校验规则
* @param Api url和method
* @param customRules 自定义规则
*/
export const getValidateRules = async (
Api: AxiosRequestConfig,
customRules?: Partial<FormSchemaExt>[],
): Promise<Partial<FormSchema>[]> => {
return new Promise(async (resolve, _reject) => {
// 转换请求路径
const formValidateApi = { url: '', method: Api.method };
for (const sp in ServicePrefixEnum) {
if (Api.url.startsWith(ServicePrefixEnum[sp])) {
formValidateApi.url = Api.url.replace(
ServicePrefixEnum[sp],
`${ServicePrefixEnum[sp]}/form/validator`,
);
}
}
try {
const key = formValidateApi.url + formValidateApi.method;
if (ruleMap.has(key)) {
return resolve(ruleMap.get(key));
}
// 请求后端的 /form/validator/xxx 接口获取 /xxx 接口的校验规则
const res = await defHttp.request<FieldValidatorDesc[]>({ ...formValidateApi });
if (res) {
// 对后端返回的校验规则进行转换
const formSchemaRules = transformationRules(res);
// 后端的规则 + 前端写死的规则,出现重复的规则,以前端为准
const allRules = enhanceCustomRules(formSchemaRules, customRules);
ruleMap.set(key, allRules);
return resolve(allRules);
}
} catch (error) {}
return resolve([]);
});
};
# formValidateService.ts
/**
* 从后端获取某个接口基于 Hibernate Validator 注解生成的参数校验规则
* @param Api url和method
* @param customRules 自定义规则
*/
export const getValidateRules = async (
Api: AxiosRequestConfig,
customRules?: Partial<FormSchemaExt>[],
): Promise<Partial<FormSchema>[]> => {
return new Promise(async (resolve, _reject) => {
// 转换请求路径
const formValidateApi = { url: '', method: Api.method };
for (const sp in ServicePrefixEnum) {
if (Api.url.startsWith(ServicePrefixEnum[sp])) {
formValidateApi.url = Api.url.replace(
ServicePrefixEnum[sp],
`${ServicePrefixEnum[sp]}/form/validator`,
);
}
}
try {
const key = formValidateApi.url + formValidateApi.method;
if (ruleMap.has(key)) {
return resolve(ruleMap.get(key));
}
// 请求后端的 /form/validator/xxx 接口获取 /xxx 接口的校验规则
const res = await defHttp.request<FieldValidatorDesc[]>({ ...formValidateApi });
if (res) {
// 对后端返回的校验规则进行转换
const formSchemaRules = transformationRules(res);
// 后端的规则 + 前端写死的规则,出现重复的规则,以前端为准
const allRules = enhanceCustomRules(formSchemaRules, customRules);
ruleMap.set(key, allRules);
return resolve(allRules);
}
} catch (error) {}
return resolve([]);
});
};
每个接口对应的校验规则请求地址有以下规则,如:
业务接口 | 校验规则接口 |
---|---|
post /api/base/user/save | post /api/base**/form/validator**/user/save |
put /api/base/user/update | put /api/base**/form/validator**/user/update |
由此可见,校验规则接口 就是在 业务接口的基础上加了 /form/validator
,请求方式不变。