laitimes

Say goodbye to if else, SpringBoot is elegant enough to do parameter validation!

author:Lao Cheng is not bug

I. Overview

When we want to provide a reliable API interface, the verification of parameters to ensure the correctness of the final data storage, is an essential activity. For example, the figure below is a function that adds a menu validation parameter to one of our projects, and writes a lot of if else for verification, which is very inelegant, and parameter verification is even more boring than boring CRUD.

This is just a verification for creating a menu, only need to determine whether the menu, menu URL and the parent class ID of the menu are empty, and whether the parent menu is mounted correctly, so that 30 or 40 lines of code have been consumed, not to mention, the interface with many parameters such as creating goods in the management background. It is estimated that several hundred lines of verification code will be written.

/**
  * 验证参数是否正确
  */
 private void verifyForm(SysMenuEntity menu){
  if(StringUtils.isBlank(menu.getName())){
   throw new RRException("菜单名称不能为空");
  }
  
  if(menu.getParentId() == null){
   throw new RRException("上级菜单不能为空");
  }
  
  //菜单
  if(menu.getType() == Constant.MenuType.MENU.getValue()){
   if(StringUtils.isBlank(menu.getUrl())){
    throw new RRException("菜单URL不能为空");
   }
  }
  
  //上级菜单类型
  int parentType = Constant.MenuType.CATALOG.getValue();
  if(menu.getParentId() != 0){
   SysMenuEntity parentMenu = sysMenuService.getById(menu.getParentId());
   parentType = parentMenu.getType();
  }
  
  //目录、菜单
  if(menu.getType() == Constant.MenuType.CATALOG.getValue() ||
    menu.getType() == Constant.MenuType.MENU.getValue()){
   if(parentType != Constant.MenuType.CATALOG.getValue()){
    throw new RRException("上级菜单只能为目录类型");
   }
   return ;
  }
  
  //按钮
  if(menu.getType() == Constant.MenuType.BUTTON.getValue()){
   if(parentType != Constant.MenuType.MENU.getValue()){
    throw new RRException("上级菜单只能为菜单类型");
   }
   return ;
  }
 }
           

Maybe the little partner will say that it is not possible to check the line without parameters? Or put the parameter verification on the front end? The world is more insecure than we think, and there may be "hackers" who bypass the browser and directly use HTTP tools to simulate requests to pass illegal parameters to the backend API interface to achieve their "ulterior" purpose. For example, SQL injection attacks, I believe, many times it is not that we do not want to add, but there is no unified and convenient way for us to quickly add the function of implementing parameter verification.

Most of the difficulties encountered in the world have already had solutions, especially for Java with a very complete development ecosystem, as early as Java 2009 proposed the Bean Validation specification, and has experienced JSR303, JSR349, JSR380 three times the top of the standard, developed to 2.0.

Say goodbye to if else, SpringBoot is elegant enough to do parameter validation!

If you are attentive, you may find that in Jakarta Bean Validation 3.0, 3.0 has not changed much, just changed the package name and namespace. It is actually an implementation of Bean Validation 2.0.

Say goodbye to if else, SpringBoot is elegant enough to do parameter validation!

Bean Validation, like the JPA we learned a long time ago, only provides specifications, does not provide specific implementation, and currently implements the data verification framework of the Bean Validation specification, mainly including:

  • Hibernate Validator
  • Apache BVal

Isn't Hibernate an old-fashioned ORM framework? Isn't it all used now? In fact, Hibernate is under the slogan Everything data, and it also provides solutions such as Hibernate Search, Hibernate OGM, and so on.

Hibernate is only used less in China, and the main thing in China is still more than the semi-ORM framework of mybatis. We can take a look at Google's trends:

Search popularity in mybatis, jpa, hibernate in China:

Say goodbye to if else, SpringBoot is elegant enough to do parameter validation!

Search popularity in mybatis, jpa, hibernate worldwide:

Say goodbye to if else, SpringBoot is elegant enough to do parameter validation!

Since the domestic development environment can be said that 99.99% of developers must be using spring, and it just so happens that Spring Validation provides built-in encapsulation support for Bean Validation, you can use @Validated annotations to implement declarative validation without directly calling the API methods provided by Bean Validation.

In terms of implementation principle, it is also based on Spring AOP interception, and finally calls different Bean Validation implementation frameworks. For example, Hibernate Validator. Implementing validation-related operations is similar to Spring Transaction transactions, which implement declarative transactions through @Transactional annotations. Let's start learning how to implement parameter validation in Spring Boot.

II. Notes

Before we get started, let's take a look at the annotations that this article might cover. Under the javax.validation.constraints package, a series of constraint annotations are defined. 22 in total, as follows:

Say goodbye to if else, SpringBoot is elegant enough to do parameter validation!

It can be roughly divided into the following categories:

2.1 Null and non-null checks

  • @NotBlank: Can only be used if the string is not null and the string #trim() after length is greater than 0.
  • @NotEmpty : The element of the collection object is not 0, that is, the collection is not empty, and can also be used for strings that are not null.
  • @NotNull : Cannot be null.
  • @Null : Must be null.

2.2 Numerical checking

  • @DecimalMax : The annotated element must be a number whose value must be less than or equal to the specified maximum value.
  • @DecimalMin (value): The annotated element must be a number whose value must be greater than or equal to the specified minimum value.
  • @Digits (integer, fraction): The annotated element must be a number whose value must be within an acceptable range.
  • @Positive: Judgment positive.
  • @PositiveOrZero : Determines a positive number or 0.
  • @Max : The value of this field can only be less than or equal to the value.
  • @Min : The value of this field can only be greater than or equal to the value. - @Negative : Judge negative numbers.
  • @NegativeOrZero : Determines a negative number or 0.

2.3 Boolean value check

  • @AssertFalse : The annotated element must be true.
  • @AssertTrue : The annotated element must be false.

2.4 Length Check

  • @Size(max, min): Check whether the size of the field is between min and max, which can be a string, array, collection, Map, etc.

2.5 Date Checking

  • @Future : The annotated element must be a date in the future.
  • @FutureOrPresent : Determines whether the date is in the future or present.
  • @Past: Check that the date of the field is in the past.
  • @PastOrPresent : Determines whether the date is past or present.

2.6 Other Examinations

  • @Email : The element to be annotated must be an email address.
  • @Pattern : The annotated element must conform to the specified regular expression.

2.7 Hibernate Validator Additional Constraint Annotations

Under the org.hibernate.validator.constraints package, a series of constraint annotations are defined. As follows:

  • @Range(min=, max=): The annotated element must be within the appropriate range.
  • @Length(min=, max=): The size of the annotated string must be within the specified range.
  • @URL(protocol=,host=,port=,regexp=,flags=): The commented string must be a valid URL.
  • @SafeHtml : Determine whether the submitted HTML is safe. For example, you can't include JavaScript scripts, etc.
Say goodbye to if else, SpringBoot is elegant enough to do parameter validation!

2.8 @Valid and @Validated

@Valid annotations, defined by Bean Validation, can be added to ordinary methods, constructors, method parameters, method returns, and member variables, indicating that they require constraint validation.

@Validated annotations, which are Spring Validation lock definitions, can be added to classes, method parameters, and normal methods to indicate that they require constraint validation. At the same time, @Validated has a value property and supports group verification. The properties are as follows:

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {

 /**
  * Specify one or more validation groups to apply to the validation step
  * kicked off by this annotation.
  * <p>JSR-303 defines validation groups as custom annotations which an application declares
  * for the sole purpose of using them as type-safe group arguments, as implemented in
  * {@link org.springframework.validation.beanvalidation.SpringValidatorAdapter}.
  * <p>Other {@link org.springframework.validation.SmartValidator} implementations may
  * support class arguments in other ways as well.
  */
 Class<?>[] value() default {};

}
           

For beginners, it's easy to confuse @Valid (under the javax.validation package) and @Validated (under the org.springframework.validation.annotation package) annotations. There are roughly the following differences:

Say goodbye to if else, SpringBoot is elegant enough to do parameter validation!

@Valid has nested object validation function For example, if you do not add a @Valid annotation on the User.profile property, it will cause the UserProfile.nickname property to be not validated.

// User.java
public class User {
    
    private String id;

    @Valid
    private UserProfile profile;

}

// UserProfile.java
public class UserProfile {

    @NotBlank
    private String nickname;

}
           

In general, in most cases, we can use @Validated annotations. In scenarios with nested checks, we use @Valid annotations to add to member attributes.

3. Quick start

3.1 Introducing dependencies

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>


    <groupId>com.ratel</groupId>
    <artifactId>java-validation</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>java-validation</name>
    <description>java validation action</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


        <!--在一些高版本springboot中默认并不会引入这个依赖,需要手动引入-->
<!--        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <scope>compile</scope>
        </dependency>-->

        <!-- 保证 Spring AOP 相关的依赖包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>


        <!--lombok相关 方便开发-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

        <!--knife4j接口文档 方便待会进行接口测试-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>

    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
           

In the Spring Boot architecture, spring-boot-starter-validation dependencies are also provided. Here, we did not introduce. Why? Because spring-boot-starter-web has already introduced spring-boot-starter-validation, and hibernate-validator dependencies have been introduced in spring-boot-starter-validation, there is no need to repeat the introduction. The dependency introduction relationship between the three can be seen in the figure below

Say goodbye to if else, SpringBoot is elegant enough to do parameter validation!

3.2 Create basic classes

UserAddDTO entity class:

package com.ratel.validation.entity;

import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;

/**
 * @Description
 * @Author ratelfu
 * @Date 2023/04/07
 * @Version 1.0
 */
@Data
public class UserAddDTO {

    /**
     * 账号
     */
    @NotEmpty(message = "登录账号不能为空")
    @Length(min = 5, max = 16, message = "账号长度为 5-16 位")
    @Pattern(regexp = "^[A-Za-z0-9]+#34;, message = "账号格式为数字以及字母")
    private String username;
    /**
     * 密码
     */
    @NotEmpty(message = "密码不能为空")
    @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
    private String password;
}
           

UserController is used to write interfaces, and on the class, add @Validated annotations, indicating that UserController is a parameter validation that all interfaces need to be validated.

package com.ratel.validation.cotroller;

import com.ratel.validation.entity.UserAddDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Min;

@RestController
@RequestMapping("/users")
@Validated
public class UserController {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @GetMapping("/get")
    public UserAddDTO get(@RequestParam("id") @Min(value = 1L, message = "编号必须大于 0") Integer id) {
        logger.info("[get][id: {}]", id);
        UserAddDTO userAddDTO = new UserAddDTO();
        userAddDTO.setUsername("张三");
        userAddDTO.setPassword("123456");
        return userAddDTO;
    }

    @PostMapping("/add")
    public void add(@Valid @RequestBody UserAddDTO addDTO) {
        logger.info("[add][addDTO: {}]", addDTO);
    }

}
           

3.3 Start the program and test

Start the program, and then in the browser we can type: swagger access address: http://localhost:8080/doc.html#/home Open the swagger document to test:

Say goodbye to if else, SpringBoot is elegant enough to do parameter validation!

First we visit http://localhost:8080/users/get?id=-1 to test, see the returned results, and sure enough check our id.

Say goodbye to if else, SpringBoot is elegant enough to do parameter validation!

Next, we visit http://localhost:8080/users/add to verify the new user: the request body we write as:

{
  "password": "233",
  "username": "33"
}
           

The result is then returned as follows:

{
  "timestamp": "2023-04-09T13:33:58.864+0000",
  "status": 400,
  "error": "Bad Request",
  "errors": [
    {
      "codes": [
        "Length.userAddDTO.password",
        "Length.password",
        "Length.java.lang.String",
        "Length"
      ],
      "arguments": [
        {
          "codes": [
            "userAddDTO.password",
            "password"
          ],
          "arguments": null,
          "defaultMessage": "password",
          "code": "password"
        },
        16,
        4
      ],
      "defaultMessage": "密码长度为 4-16 位",
      "objectName": "userAddDTO",
      "field": "password",
      "rejectedValue": "233",
      "bindingFailure": false,
      "code": "Length"
    },
    {
      "codes": [
        "Length.userAddDTO.username",
        "Length.username",
        "Length.java.lang.String",
        "Length"
      ],
      "arguments": [
        {
          "codes": [
            "userAddDTO.username",
            "username"
          ],
          "arguments": null,
          "defaultMessage": "username",
          "code": "username"
        },
        16,
        5
      ],
      "defaultMessage": "账号长度为 5-16 位",
      "objectName": "userAddDTO",
      "field": "username",
      "rejectedValue": "33",
      "bindingFailure": false,
      "code": "Length"
    }
  ],
  "message": "Validation failed for object='userAddDTO'. Error count: 2",
  "path": "/users/add"
}
           

Returns the errors field in the JSON string of the result, and an array of parameter error details. Each array element corresponds to a parameter error detail. Here, username violates the 5-16 digit rule for account length. Password violates the 4-16 digits password length.

Schematic diagram of the returned result:

Say goodbye to if else, SpringBoot is elegant enough to do parameter validation!

3.3 Some doubts

Here the attentive little partner may have several questions:

3.3.1 Question 1

#get (id) method, we did not add @Valid annotations to id, while on the #add(addDTO) method, we added @Valid annotations to addDTO. Why is this discrepancy?

Because UserController uses @Validated annotations, Spring Validation uses AOP for faceting and parameter validation. The interceptor of this section uses the MethodValidationInterceptor.

  • For the #get(id) method, the parameter IDs that need to be verified are tiled, so there is no need to add @Valid annotations.
  • For the #add (addDTO) method, the parameter addDTO that needs to be verified is actually equivalent to nested verification, and the parameters to be verified are all in addDTO, so you need to add @Valid (in fact, the measured plus @Validated is also OK, I don't know why for the sake of good distinction, first use @Valid ) annotations.

3.3.2 Question 2

The #get(id) method returns status = 500, while the #add(addDTO) method returns status = 400.

  • For #get(id) methods, in the MethodValidationInterceptor interceptor, the ConstraintViolationException exception is thrown when the parameter is incorrect.
  • For the #add(addDTO) method, because addDTO is a POJO object, it will follow SpringMVC's DataBinder mechanism, which will call DataBinder#validate(Object... validationHints) method, which performs validation. When the validation fails, a BindException is thrown.

In SpringMVC, exceptions are handled by default using DefaultHandlerExceptionResolver.

  • For BindException exceptions, a status code of 400 is handled.
  • For ConstraintViolationException exceptions, there is no special handling, so it is handled as a status code of 500.

Here, we are throwing a question, if the #add(addDTO) method, if the parameters are correct, after completing the parameter verification in the DataBinder, will it go through the interceptor of the MethodValidationInterceptor? Think 100 milliseconds...

The answer is yes. In this way, it leads to waste. So in the Controller class, if there is only nested validation for similar #add (addDTO) methods, then I can not add @Validated annotations to the Controller class. Therefore, only DataBinder is used for parameter validation.

3.3.3 The return prompt is unfriendly and too long

Third, whether it is the #get(id) method or the #add(addDTO) method, their return prompts are very unfriendly, so what to do? We will handle this in Section 4 by Handling Validation Exceptions.

4. Handle verification exceptions

4.1 Enumeration classes that fail validation

package com.ratel.validation.enums;

/**
 * 业务异常枚举
 */
public enum ServiceExceptionEnum {

    // ========== 系统级别 ==========
    SUCCESS(0, "成功"),
    SYS_ERROR(2001001000, "服务端发生异常"),
    MISSING_REQUEST_PARAM_ERROR(2001001001, "参数缺失"),
    INVALID_REQUEST_PARAM_ERROR(2001001002, "请求参数不合法"),

    // ========== 用户模块 ==========
    USER_NOT_FOUND(1001002000, "用户不存在"),

    // ========== 订单模块 ==========

    // ========== 商品模块 ==========
    ;

    /**
     * 错误码
     */
    private final int code;
    /**
     * 错误提示
     */
    private final String message;

    ServiceExceptionEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

}
           

4.2 Unified return result entity classes

package com.ratel.validation.common;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.util.Assert;

import java.io.Serializable;

/**
 * 通用返回结果
 *
 * @param <T> 结果泛型
 */
public class CommonResult<T> implements Serializable {

    public static Integer CODE_SUCCESS = 0;

    /**
     * 错误码
     */
    private Integer code;
    /**
     * 错误提示
     */
    private String message;
    /**
     * 返回数据
     */
    private T data;

    /**
     * 将传入的 result 对象,转换成另外一个泛型结果的对象
     *
     * 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
     *
     * @param result 传入的 result 对象
     * @param <T> 返回的泛型
     * @return 新的 CommonResult 对象
     */
    public static <T> CommonResult<T> error(CommonResult<?> result) {
        return error(result.getCode(), result.getMessage());
    }

    public static <T> CommonResult<T> error(Integer code, String message) {
        Assert.isTrue(!CODE_SUCCESS.equals(code), "code 必须是错误的!");
        CommonResult<T> result = new CommonResult<>();
        result.code = code;
        result.message = message;
        return result;
    }

    public static <T> CommonResult<T> success(T data) {
        CommonResult<T> result = new CommonResult<>();
        result.code = CODE_SUCCESS;
        result.data = data;
        result.message = "";
        return result;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    @JsonIgnore
    public boolean isSuccess() {
        return CODE_SUCCESS.equals(code);
    }

    @JsonIgnore
    public boolean isError() {
        return !isSuccess();
    }

    @Override
    public String toString() {
        return "CommonResult{" +
                "code=" + code +
                ", message='" + message + '\'' +
                ", data=" + data +
                '}';
    }

}
           

4.3 Add the global exception handling class GlobalExceptionHandler

package com.ratel.validation.exception;


import com.ratel.validation.common.CommonResult;
import com.ratel.validation.enums.ServiceExceptionEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

@ControllerAdvice(basePackages = "com.ratel.validation.cotroller")
public class GlobalExceptionHandler {

    private Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 处理 MissingServletRequestParameterException 异常
     *
     * SpringMVC 参数不正确
     */
    @ResponseBody
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public CommonResult missingServletRequestParameterExceptionHandler(HttpServletRequest req, MissingServletRequestParameterException ex) {
        logger.error("[missingServletRequestParameterExceptionHandler]", ex);
        // 包装 CommonResult 结果
        return CommonResult.error(ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getCode(),
                ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = ConstraintViolationException.class)
    public CommonResult constraintViolationExceptionHandler(HttpServletRequest req, ConstraintViolationException ex) {
        logger.error("[constraintViolationExceptionHandler]", ex);
        // 拼接错误
        StringBuilder detailMessage = new StringBuilder();
        for (ConstraintViolation<?> constraintViolation : ex.getConstraintViolations()) {
            // 使用 ; 分隔多个错误
            if (detailMessage.length() > 0) {
                detailMessage.append(";");
            }
            // 拼接内容到其中
            detailMessage.append(constraintViolation.getMessage());
        }
        // 包装 CommonResult 结果
        return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(),
                ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString());
    }

    @ResponseBody
    @ExceptionHandler(value = BindException.class)
    public CommonResult bindExceptionHandler(HttpServletRequest req, BindException ex) {
        logger.info("========进入了 bindException======");
        logger.error("[bindExceptionHandler]", ex);
        // 拼接错误
        StringBuilder detailMessage = new StringBuilder();
        for (ObjectError objectError : ex.getAllErrors()) {
            // 使用 ; 分隔多个错误
            if (detailMessage.length() > 0) {
                detailMessage.append(";");
            }
            // 拼接内容到其中
            detailMessage.append(objectError.getDefaultMessage());
        }
        // 包装 CommonResult 结果
        return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(),
                ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString());
    }

    @ResponseBody
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public CommonResult MethodArgumentNotValidExceptionHandler(HttpServletRequest req, MethodArgumentNotValidException ex) {
        logger.info("-----------------进入了 MethodArgumentNotValidException-----------------");
        logger.error("[MethodArgumentNotValidException]", ex);
        // 拼接错误
        StringBuilder detailMessage = new StringBuilder();
        for (ObjectError objectError : ex.getBindingResult().getAllErrors()) {
            // 使用 ; 分隔多个错误
            if (detailMessage.length() > 0) {
                detailMessage.append(";");
            }
            // 拼接内容到其中
            detailMessage.append(objectError.getDefaultMessage());
        }
        // 包装 CommonResult 结果
        return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(),
                ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString());
    }


    /**
     * 处理其它 Exception 异常
     * @param req
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public CommonResult exceptionHandler(HttpServletRequest req, Exception e) {
        // 记录异常日志
        logger.error("[exceptionHandler]", e);
        // 返回 ERROR CommonResult
        return CommonResult.error(ServiceExceptionEnum.SYS_ERROR.getCode(),
                ServiceExceptionEnum.SYS_ERROR.getMessage());
    }
}
           

4.4 Testing

Access: http://localhost:8080/users/add You can see that the abnormal result has been spliced into a string, which is much fresher and easier to understand than before.

Say goodbye to if else, SpringBoot is elegant enough to do parameter validation!
Say goodbye to if else, SpringBoot is elegant enough to do parameter validation!
Source: blog.csdn.net/weter_drop/article/details/130046637