当前位置: 首页 > wzjs >正文

一级a做爰片偷拍免费网站百度收录是什么意思

一级a做爰片偷拍免费网站,百度收录是什么意思,河北石家庄最新数据消息,品牌建设工作机制1.概述 书接上回,我们总结了后端接口参数校验的重要性,详解讲述了Spring Boot项目中如何整合Spring-Validator组件进行参数校验,实战教程:后端接口没做参数检验导致服务雪崩,被批评代码健壮性太差… 因为参数校验是W…

1.概述

书接上回,我们总结了后端接口参数校验的重要性,详解讲述了Spring Boot项目中如何整合Spring-Validator组件进行参数校验,实战教程:后端接口没做参数检验导致服务雪崩,被批评代码健壮性太差…

因为参数校验是Web开发中保证数据完整性和安全性的重要环节,所以Spring Boot基于**JSR-380(Bean Validation 2.0)**规范,提供了强大的参数校验机制,支持:

声明式校验(通过注解)

嵌套校验(参数对象多级)

分组校验(不同场景不同规则)

自定义校验逻辑(扩展ConstraintValidator

国际化错误消息(支持多语言)

✔的已经在入门实战教程中总结过了,不清楚的自行跳转查看,今天我们结合实际项目开发,谈谈自定义注解校验特定场景规则和国际化多语言错误消息处理。在此之前,我们先来看看Spring Boot参数校验原理

Spring Boot参数校验原理

接口参数校验属于web应用的范畴,所以对于最常用的@RequestBody参数对象校验肯定是在Spring MVC组件中实现的,RequestResponseBodyMethodProcessor是用来解析参数@RequestBody和处理响应@ResponseBody的核心所在,所以参数校验的逻辑也一定在这里解析参数的方法里:

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {......@Overridepublic Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {parameter = parameter.nestedIfOptional();// 入参转换成对象Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());String name = Conventions.getVariableNameForParameter(parameter);if (binderFactory != null) {WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);if (arg != null) {// 参数检验validateIfApplicable(binder, parameter);if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());}}if (mavContainer != null) {mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());}}return adaptArgumentIfNecessary(arg, parameter);}......
}

参数检验的方法:validateIfApplicable(binder, parameter):

protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {// 获取参数注解Annotation[] annotations = parameter.getParameterAnnotations();for (Annotation ann : annotations) {// 获取@Validated注解Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);// 有@Validated直接开启校验。// 没有再判断参数前是否有Valid起头的注解。if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});// 执行校验binder.validate(validationHints);break;}}
}

这里也是@Validated@Valid都能开启参数检验的逻辑所在。

跟着执行校验代码binder.validate(validationHints), 最终来到了LocalValidatorFactoryBean的验证方法:

@Override
public void validate(Object target, Errors errors, Object... validationHints) {if (this.targetValidator != null) {processConstraintViolations(// 进入Hibernate Validator执行真正的校验this.targetValidator.validate(target, asValidationGroups(validationHints)), errors);}
}

来到MetaConstraintdoValidateConstraint()方法:

  private boolean doValidateConstraint(ValidationContext<?> executionContext, ValueContext<?, ?> valueContext) {valueContext.setConstraintLocationKind(this.getConstraintLocationKind());boolean validationResult = this.constraintTree.validateConstraints(executionContext, valueContext);return validationResult;}

最后执行ConstraintTreevalidateSingleConstraint():

  protected final <V> Optional<ConstraintValidatorContextImpl> validateSingleConstraint(ValueContext<?, ?> valueContext, ConstraintValidatorContextImpl constraintValidatorContext, ConstraintValidator<A, V> validator) {boolean isValid;try {// 获取参数值V validatedValue = (V)valueContext.getCurrentValidatedValue();// 具体约束逻辑实现isValid = validator.isValid(validatedValue, constraintValidatorContext);} catch (RuntimeException e) {if (e instanceof ConstraintDeclarationException) {throw e;}throw LOG.getExceptionDuringIsValidCallException(e);}return !isValid ? Optional.of(constraintValidatorContext) : Optional.empty();}

可以看到真正验证参数是否合法逻辑在ConstraintValidatorisValid(),这也是我们自定义注解验证特定场景需要实现的接口逻辑所在哦。

关于方法级别requestParam/PathVariable参数校验大家自行了解,入口在MethodValidationPostProcessor这个切面

3.自定义注解验证进阶实践

在真实的项目开发中,业务场景需求是多种多样的,校验框架提供的原生注解不一定能满足复杂场景的校验,这时候我们只能自定义一个注解来实现该场景的参数校验,这里我列举两个实际开发中经常用到的。

3.1 枚举值合法性校验

在接口入参中,枚举字段很常见,比如入门教程里面的userParam的性别字段:

/** 性别  0:男生  1:女生 */
@NotNull(message = "性别不能为空")
private Integer gender;

使用了@NotNull校验了参数不能为空,但是并没有对性别的枚举值进行校验,要是传一个2上来没校验直接落库,就会产生了非法数据”不男不女“了,后患无穷~~~所以我们需要对参数值进行校验,必须武装严谨到牙齿,哈哈。

基于上面的实现原理和原生注解的实现套路,我们首先先定一个注解标记验证字段:

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {EnumValueValidator.class})
public @interface EnumValue {String message() default "enum value is not valid";/** 关联的枚举类  CheckEnumValue是我们定义的公共枚举接口,所以枚举类都要实现提供返回枚举值的方法   */Class<? extends CheckEnumValue> linkEnum() default CheckEnumValue.class;Class<?>[] groups() default { };Class<? extends Payload>[] payload() default { };
}

该注解和框架原生提供的注解套路几乎一致,这里只是多了一个验证字段关联的枚举类的属性linkEnum

接下来定义具体约束校验逻辑:

public class EnumValueValidator implements ConstraintValidator<EnumValue, Object> {private Class<? extends CheckEnumValue> clz;@Overridepublic void initialize(EnumValue constraintAnnotation) {clz = constraintAnnotation.linkEnum();}@Overridepublic boolean isValid(Object value, ConstraintValidatorContext context) {// 参数值为空不校验if (Objects.isNull(value)) {return true;}// 关联的不是枚举类不校验if (!clz.isEnum()) {return true;}CheckEnumValue[] enumConstants = clz.getEnumConstants();if (enumConstants == null || enumConstants.length == 0) {return true;}CheckEnumValue enumConstant = enumConstants[0];List enumValue = enumConstant.getEnumValue();// 判断参数值是否在枚举值中if (CollectionUtils.isEmpty(enumValue)) {return true;}if (enumValue.contains(value)) {return true;}return false;}
}

枚举校验的公共接口,枚举类都要实现该接口,该接口返回所有枚举值

public interface CheckEnumValue<T> {List<T> getEnumValue();}

性别的枚举类如下:

public enum GenderEnum implements CheckEnumValue<Integer> {MAN(0, "男生"),WOMAN(1, "女生");private Integer code;private String name;GenderEnum(Integer code, String name) {this.code = code;this.name = name;}@Overridepublic List<Integer> getEnumValue() {return Stream.of(GenderEnum.values()).map(genderEnum -> genderEnum.code).collect(Collectors.toList());}
}

接下来我们就可以在接口参数对象使用自定义注解进行枚举字段校验了

/** 性别  0:男生  1:女生 */
@EnumValue(message = "性别枚举值不对", linkEnum = GenderEnum.class)
@NotNull(message = "性别不能为空")
private Integer gender;

调接口输入参数:

{"gender":2,
}

接口放回结果如下:

{"code": 400,"msg": "Bad Request","data": {"gender": "性别枚举值不对"}
}

可以看出,我们自定义注解正常运作了。

3.2 字段联合校验

上篇文章中我们提到了因为后端没有参数校验导致服务崩溃不可用,其实接口是使用了validator进行参数校验的,但有一种特殊情况,当其中一个字段的入参等于某个值的时候,另一个字段不能为空,这种情况框架提供的基本注解解决不了,只能在接口代码写代码判断,还是userParam为例,要求输入性别为女生gender=1时,出生日期birthday必传:

if (Objects.equals(userParam.getGender(), 1) && Objects.isNull(userParam.getBirthday())) {throw new BizException("出生日期不能为空");}

可惜用了框架校验,再在接口里面写参数校验代码就显得繁杂,不够优雅,所以就没写最后就出问题了~~~

要想解决这个问题,我们只能自定义注解来实现这个复杂场景校验,同时又要不失优雅。

首先定义一个多字段组合验证注解

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {CombineNotNullValidator.class})
public @interface CombineNotNull {String message() default "enum value is not valid";/** Spring SpEL表达式   */@Language("SpEL")String condition() default "";Class<?>[] groups() default { };Class<? extends Payload>[] payload() default { };
}

实现约束校验逻辑:

public class CombineNotNullValidator implements ConstraintValidator<CombineNotNull, Object> {// 解析SpEL有一定开销, 缓存表达式private static final ConcurrentHashMap<String, Expression> EXPRESSION_CACHE = new ConcurrentHashMap<>();// SpelExpressionParser是线程安全的private static final ExpressionParser parser = new SpelExpressionParser();// 条件表达式字符串private String condition;@Overridepublic void initialize(CombineNotNull constraintAnnotation) {condition = constraintAnnotation.condition();}@Overridepublic boolean isValid(Object value, ConstraintValidatorContext context) {// 表达式为空if (StringUtils.isBlank(condition)) {return true;}// 获取入参对象(被校验的对象)RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();Object requestBody = requestAttributes.getAttribute("request_body", RequestAttributes.SCOPE_REQUEST);if (requestBody == null) {return true;}Expression expression = EXPRESSION_CACHE.computeIfAbsent(condition,k -> parser.parseExpression(k));Boolean result = expression.getValue(requestBody, Boolean.class);if (Boolean.FALSE.equals(result)) {return true;}return value != null;}
}

框架提供的ConstraintValidatorContext context上下文并没有提供入参对象,直接获取不了,所以只能我们自己实现传递入参对象上下文,实现传递方式很多,大家可以自行实现,我这里使用RequestBodyAdvice实现:

@RestControllerAdvice
public class RequestBodyHandlerAdvice implements RequestBodyAdvice {@Overridepublic boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {return true;}@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {return inputMessage;}@Overridepublic Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {// 判断接口有没有开启接口参数校验Valid valid = parameter.getParameterAnnotation(Valid.class);Validated validated = parameter.getParameterAnnotation(Validated.class);if (valid != null || validated != null) {// 传递入参对象RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();requestAttributes.setAttribute("request_body", body, RequestAttributes.SCOPE_REQUEST);}return body;}@Overridepublic Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {return null;}
}

RequestBodyAdvice不太了解的,可以查看我们之前的总结:一文带你掌握SpringMVC扩展点RequestBodyAdvice和ResponseBodyAdvice如何使用及实现原理

接下来就可以接口参数类上直接使用:

@CombineNotNull(message = "出生日期不能为空", condition = "#this.gender == 1")
private Date birthday;

当我们调接口入参如下:

{"gender":1,
}

接口返回如下:

{"code": 400,"msg": "Bad Request","data": {"birthday": "出生日期不能为空"}
}

完美解决多字段联合校验问题。

4.国际化多语言

如果你的项目是一个国际化的应用,那就必须考虑多语言了,可以使用国际化 (i18n) 以用户首选语言显示错误消息。

在项目的资源目录resources配置国际化资源文件:

messages.properties(默认)

user.id.notNull= id not be null

messages_zh_CN.properties(中文)

user.id.notNull=用户id不能为空

调整入参检验:

@NotNull(message = "{user.id.notNull}", groups = {Update.class})
private Long id;

调接口入参不输入id,提示如下:

{"code": 400,"msg": "Bad Request","data": {"id": "用户id不能为空"}
}

当发生验证错误时,错误消息将根据随请求发送的“Accept-Language”标头以用户的首选语言显示。

5.总结

Spring Boot参数校验既灵活又强大,合理使用可以大幅提升代码健壮性和可维护性! 🚀基于入门实战教程,使用@Validated完成接口常规场景接口参数校验,与此同时我们深入了解了validator实现原理,实现自定义验证注解解决特定场景业务需求,做到了代码优雅简洁、规范健壮,最终提高了系统的稳定性和可维护性。

http://www.dtcms.com/wzjs/154621.html

相关文章:

  • 建设外包网站免费收录平台
  • 云安区学校网站建设统计表网站seo提升
  • 武汉建设网站制作seo课程培训要多少钱
  • 用dreamweaver做网站怎么切块平台推广是什么意思
  • 网站搭建代码大全临沂色度广告有限公司
  • 有必要自建网站做导购吗免费换友情链接
  • 顺德网站建设案例南宁网络推广平台
  • 自建站公司北京网站外包
  • 淘客做自己的网站自己做的网站怎么推广
  • 外包兼职做图的网站网络营销成功的案例分析
  • 浏览器正能量网站2021站长之家最新域名查询
  • 昆山智能网站开发怎么开网站
  • 电商优惠券网站 建设百度关键词搜索排名查询
  • 中小企业网站建设服务培训计划方案模板
  • 昆山专业网站建设公司哪家好百度网站怎么提升排名
  • 用JS做的购物网站夜狼seo
  • 贵州城市建设网站网站seo优化运营
  • 网站免费在线客服系统seo技术培训机构
  • 旅游网站建设的意义app推广是什么意思
  • 网站建设工作年报百度移动端排名
  • 招商网站的建设意义企业seo顾问服务
  • 网站开发模式有什么站长工具查询网站
  • 社交网站设计百度关键词排名怎么靠前
  • wordpress 相关产品天津优化公司
  • 网站建设有几种方案58同城黄页推广
  • 蓝奏云注册网站seo搜论坛
  • 男朋友说是做竞彩网站维护的app营销
  • 景区微网站 建设方案百度seo关键词排名查询工具
  • 北京网站建设手机app短视频营销常用平台有
  • 陕西省住房和城乡建设委员会网站推广产品怎么发朋友圈