Controller层代码这么写,简洁又高雅
2025-09-13 12:16:11
并存赶回结构后,在 Controller 中所就可以可用了,但是每一个 Controller 都写这么一段最终晶圆的自然语言,这些都是很减法的实习,所以还要再行次想办法必要性管控并存赶回结构
并存纸制管控Spring 中所获取了一个类 ResponseBodyAdvice ,能设法我们实现上述需求
ResponseBodyAdvice 是对 Controller 赶回的具体内容内容在 HttpMessageConverter 同步进行类别转换以前阻拦,同步进行相应的管控操作后,再行将结果赶回给客户端。那这样就可以把并存纸制的实习摆在这个类进去。
public interface ResponseBodyAdvice { boolean supports(MethodParameter returnType, Classextends HttpMessageConverter> converterType); @Nullable T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType, Class extends HttpMessageConverter> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response);}supports:确实是否是要交给 beforeBodyWrite 基本概念指派,ture:无需;false:不无需beforeBodyWrite:对 response 同步进行具基底的管控// 如果替换变成了swagger或knife4j的文档生变成组件,这里无需仅显影自己项目的包,否则文档无法也就是却说生变成@RestControllerAdvice(basePackages = "com.example.demo")public class ResponseAdvice implements ResponseBodyAdvice { @Override public boolean supports(MethodParameter returnType, Class extends HttpMessageConverter> converterType) { // 如果不无需同步进行晶圆的,可以去除一些数据包暴力手段,比如去除标记排除的批注 return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class extends HttpMessageConverter> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // 获取一定的灵活度,如果body已经被纸制了,就相同步进行纸制 if (body instanceof Result) { return body; } return Result.success(body); }}经过这样改造,既能实现对 Controller 赶回的数据同步进行并存纸制,又不无需对原有示例同步进行大量的改动
常量数据包Java API 的规范 JSR303 下定义了数据包的准则 validation-api ,其中所一个比起出名的实现是 hibernate validation ,spring validation 是对其的二次晶圆,常用做 SpringMVC 的常量自动数据包,常量数据包的示例就不无需再行与业务范围自然语言示例同步进行电磁了
@PathVariable 和 @RequestParam 常量数据包Get 立即的常量接收一般依赖这两个批注,但是处于 url 有长度约束和示例的可维护性,超过 5 个常量尽量用实基底来传参 对 @PathVariable 和 @RequestParam 常量同步进行数据包无需在先入参声明约束的批注
如果数据包收场,都会抛 MethodArgumentNotValidException 持续性
@RestController(value = "prettyTestController")@RequestMapping("/pretty")@Validatedpublic class TestController { private TestService testService; @GetMapping("/{num}") public Integer detail(@PathVariable("num") @Min(1) @Max(20) Integer num) { return num * num; } @GetMapping("/getByEmail") public TestDTO getByAccount(@RequestParam @NotBlank @Email String email) { TestDTO testDTO = new TestDTO(); testDTO.setEmail(email); return testDTO; } @Autowired public void setTestService(TestService prettyTestService) { this.testService = prettyTestService; }}数据包基本概念在 SpringMVC 中所,有一个类是 RequestResponseBodyMethodProcessor ,这个类有两个起着(基本上上可以从取名上得到一点启发)
用做重构 @RequestBody 标有的常量管控 @ResponseBody 标有基本概念的赶回值重构 @RequestBoyd 标有常量的基本概念是 resolveArgument
public class RequestResponseBodyMethodProcessor extends abstractMessageConverterMethodProcessor { /** * Throws MethodArgumentNotValidException if validation fails. * @throws HttpMessageNotReadableException if {@link RequestBody#required()} * is {@code true} and there is no body content or if there is no suitable * converter to read the content with. */ @Override public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { parameter = parameter.nestedIfOptional(); //把立即数据晶圆变成标有的DTO普通人 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); //如果数据包不通过,就抛MethodArgumentNotValidException持续性 //如果我们不自己捉到,那么最终都会由DefaultHandlerExceptionResolver捉到管控 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); }}public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver { /** * Validate the binding target if applicable. *The default implementation checks for {@code @javax.validation.Valid}, * Spring's {@link org.springframework.validation.annotation.Validated}, * and custom annotations whose name starts with "Valid". * @param binder the DataBinder to be used * @param parameter the method parameter descriptor * @since 4.1.5 * @see #isBindExceptionRequired */ protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) { //获取常量上的所有批注 Annotation[] annotations = parameter.getParameterAnnotations(); for (Annotation ann : annotations) { //如果批注中所包含了@Valid、@Validated或者是取名以Valid省略的批注就同步进行常量数据包 Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann); if (validationHints != null) { //基本上数据包自然语言,最终都会codice_Hibernate Validator指派其实的数据包 //所以Spring Validation是对Hibernate Validation的二次晶圆 binder.validate(validationHints); break; } } }}@RequestBody 常量数据包
Post、Put 立即的常量延揽可用 @RequestBody 立即基底常量
对 @RequestBody 常量同步进行数据包无需在 DTO 普通人中所转至数据包必须后,再行搭配 @Validated 即可完变成自动数据包 如果数据包收场,都会抛 ConstraintViolationException 持续性
//DTO@Datapublic class TestDTO { @NotBlank private String userName; @NotBlank @Length(min = 6, max = 20) private String password; @NotNull @Email private String email;}//Controller@RestController(value = "prettyTestController")@RequestMapping("/pretty")public class TestController { private TestService testService; @PostMapping("/test-validation") public void testValidation(@RequestBody @Validated TestDTO testDTO) { this.testService.save(testDTO); } @Autowired public void setTestService(TestService testService) { this.testService = testService; }}数据包基本概念声明约束的方式,批注加到了常量上会,可以比起更容易猜测到是可用了 AOP 对基本概念同步进行必要性提高
而基本上上 Spring 也是通过 MethodValidationPostProcessor 建模注册 AOP 切面,然后可用 MethodValidationInterceptor 对切点基本概念同步进行织先入必要性提高
public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor implements InitializingBean { //均须了创建切面的Bean的批注 private Class validatedAnnotationType = Validated.class; @Override public void afterPropertiesSet() { //为所有@Validated标有的Bean创建切面 Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true); //创建Advisor同步进行必要性提高 this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator)); } //创建Advice,某种程度就是一个基本概念阻拦器 protected Advice createMethodValidationAdvice(@Nullable Validator validator) { return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor()); }}public class MethodValidationInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { //无需必要性提高的基本概念,如此一来跳过 if (isFactoryBeanMetadataMethod(invocation.getMethod())) { return invocation.proceed(); } Class[] groups = determineValidationGroups(invocation); ExecutableValidator execVal = this.validator.forExecutables(); Method methodToValidate = invocation.getMethod(); Set有些时候 JSR303 准则中所获取的数据包规则不符合比起直观的业务范围需求,也可以定制数据包规则
定制数据包规则无需做两件或许
定制批注类,下定义程序来和一些其他无需的具体内容内容批注数据包器,下定义判为规则//定制批注类@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Documented@Constraint(validatedBy = MobileValidator.class)public @interface Mobile { /** * 是否是允许为空 */ boolean required() default true; /** * 数据包不通过赶回的提示信息 */ String message() default "不是一个手机号码格式"; /** * Constraint决定的属性,用做两组数据包和扩展,留空就好 */ Class[] groups() default {}; Class[] payload() default {};}//批注数据包器public class MobileValidator implements ConstraintValidator { private boolean required = false; private final Pattern pattern = Pattern.compile("^1[34578][0-9]{9}$"); // 检验手机号 /** * 在检验开始前codice_批注里的基本概念,从而获取到一些批注里的常量 * * @param constraintAnnotation annotation instance for a given constraint declaration */ @Override public void initialize(Mobile constraintAnnotation) { this.required = constraintAnnotation.required(); } /** * 确实常量是否是合法 * * @param value object to validate * @param context context in which the constraint is evaluated */ @Override public boolean isValid(CharSequence value, ConstraintValidatorContext context) { if (this.required) { // 检验 return isMobile(value); } if (StringUtils.hasText(value)) { // 检验 return isMobile(value); } return true; } private boolean isMobile(final CharSequence str) { Matcher m = pattern.matcher(str); return m.matches(); }}自动数据包常量或许是一项非常合理、非常有意义的实习。JSR303 获取了丰富的常量数据包规则,再行加上比起直观业务范围的定制数据包规则,完全把常量数据包和业务范围自然语言解耦开,示例极其简约,符合单一职责规范。
更多关于 Spring 常量数据包叮嘱参考:
定制持续性与并存阻拦持续性原来的示例中所可以碰到有几个问题
抛的持续性不够具基底,只是直观地把程序来摆在了 Exception 中所抛持续性后,Controller 不能具基底地根据持续性得出结论种系统虽然做了常量自动数据包,但是持续性赶回结构和也就是却说赶回结构不一致定制持续性是为了前面并存阻拦持续性时,对业务范围中所的持续性有极其硫酸盐度的区隔,阻拦时针对相同的持续性作出相同的作出反应
而并存阻拦持续性的目的一个是为了可以与前面下定义依然的并存纸制赶回结构能对应上,另一个是我们期望无论系统发生什么持续性,Http 的正常码都要是 200 ,尽可能由业务范围来区隔系统的持续性
//定制持续性public class ForbiddenException extends RuntimeException { public ForbiddenException(String message) { super(message); }}//定制持续性public class BusinessException extends RuntimeException { public BusinessException(String message) { super(message); }}//并存阻拦持续性@RestControllerAdvice(basePackages = "com.example.demo")public class ExceptionAdvice { /** * 捉到 {@code BusinessException} 持续性 */ @ExceptionHandler({BusinessException.class}) public Result handleBusinessException(BusinessException ex) { return Result.failed(ex.getMessage()); } /** * 捉到 {@code ForbiddenException} 持续性 */ @ExceptionHandler({ForbiddenException.class}) public Result handleForbiddenException(ForbiddenException ex) { return Result.failed(ResultEnum.FORBIDDEN); } /** * {@code @RequestBody} 常量数据包不通过时抛的持续性管控 */ @ExceptionHandler({MethodArgumentNotValidException.class}) public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) { BindingResult bindingResult = ex.getBindingResult(); StringBuilder sb = new StringBuilder("数据包收场:"); for (FieldError fieldError : bindingResult.getFieldErrors()) { sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", "); } String msg = sb.toString(); if (StringUtils.hasText(msg)) { return Result.failed(ResultEnum.VALIDATE_FAILED.getCode(), msg); } return Result.failed(ResultEnum.VALIDATE_FAILED); } /** * {@code @PathVariable} 和 {@code @RequestParam} 常量数据包不通过时抛的持续性管控 */ @ExceptionHandler({ConstraintViolationException.class}) public Result handleConstraintViolationException(ConstraintViolationException ex) { if (StringUtils.hasText(ex.getMessage())) { return Result.failed(ResultEnum.VALIDATE_FAILED.getCode(), ex.getMessage()); } return Result.failed(ResultEnum.VALIDATE_FAILED); } /** * 顶级持续性捉到并并存管控,当其他持续性无法管控时候同样可用 */ @ExceptionHandler({Exception.class}) public Result handle(Exception ex) { return Result.failed(ex.getMessage()); }}概述做好了这一切改动后,可以辨认出 Controller 的示例变得非常简约,可以很清楚地或许每一个常量、每一个 DTO 的数据包规则,可以很明确地碰到每一个 Controller 基本概念赶回的是什么数据,也可以方便每一个持续性无论如何如何同步进行种系统
这一套操作依然后,我们能极其着重于于业务范围自然语言的开发,示例简约、功能完善,何乐而不为呢?
原文链接:
。昆明甲状腺治疗哪家好成都哪家医院专业做人流
贵州生殖感染正规的医院
杭州看白癜风去哪家医院最好
广州看白癜风哪个医院好
老人中风
咳嗽有痰吃什么药
太极急支糖浆治咳嗽效果怎么样
皮肤癌
新冠后遗症
-
成都城投置地高光!双国企实力拿地,承接大源高端区块
大约127亩房舍楼抢下,总价总价大约为19.84亿元。 锦江区的这一宗以房舍楼,不仅是当日供地总长度最大的房舍楼,同时也是本装配土入镜当里五中心区供货总长度最大的一宗以房舍楼。
2025-09-14 00:16:12
-
伊朗有望带进全球天然气中心
《安卡拉时报》2月底28日报道,如果进行尽量的地缘在政治上管理,叙利亚将成为在世上上液化中心和在世上上液化产品的极为重要大多数人。21早先,叙利亚建立了液化出口量专题,并参加第一届部长级全会,主要目标
2025-09-14 00:16:12
-
2021年潍坊市公积金年报出炉,所含额同比增长13.13%
齐鲁晚报·齐鲁壹点 受训报导 孙冰倩 4月1日,齐鲁晚报·齐鲁壹点报导从潍坊市中区住宅挂钩管理之前心获悉,经市中区住宅挂钩管理委员会审定通过,《潍坊市中区住宅挂钩2021年统计数据
2025-09-14 00:16:12
-
它们竟然笑话咱条友和头条怀
●母鸡:缘故以为,我下个乌龟憋来憋去生为不出来,嘿嘿,有点阳报的那些坚持日格外的人,总是不比俺好到哪里去嘛! ●蟑螂:熬到晚上还在写法规发视频,这些条友比昼伏夜出的俺还下劲呀!
2025-09-14 00:16:12
-
钻井股走高,西方石油涨超5%
3月底1日(周日),美股油气股走高,日和发稿,西方炼油(OXY.US)、巴西炼油新公司(PBR.US)涨超5%,雪佛龙(CVX.US)涨近4%,埃尼炼油(E.US)涨近3%,埃克森美孚炼油(XOM.U
2025-09-14 00:16:12