Springboot implements elegant parameter verification (Spring Validation) and says goodbye to if else

I. Overview

When we want to provide a reliable API interface and verify the parameters to ensure the correctness of the final data storage, it is 必不可少a . For example, the picture below shows a new menu verification parameter function in one of our projects. I wrote a lot of if else for verification, which is very inelegant. Compared with boring CRUD, parameter verification is even more boring. This is just a verification of creating a menu. It only needs to judge whether the menu, the menu url and the parent id of the menu are empty, and whether the upper-level menu is mounted correctly. This has consumed 30 or 40 lines of code, not to mention, the management Create an interface with many parameters such as commodities in the background. It is estimated that hundreds of 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 friends will say that it is okay to not add parameter verification? Or put the parameter verification on the front end? Then your thinking is too simple. The world is less safe than we imagined. There may be "hackers" who will bypass the browser and directly use HTTP tools to simulate requests and pass illegal parameters to the back-end API interface to reach them. “不可告人”purpose . For example, sql injection attack , I believe, many times it is not that we don't want to add it, but that there is no unified and convenient way for us to quickly add the function of realizing parameter verification.

Most of the difficulties encountered in the world already have solutions, especially for java, which has a very complete development ecology. As early as 2009, Java proposed the Bean Validation specification, and it has gone through three standards of JSR303, JSR349, and JSR380. Stick to the top and develop to 2.0.
insert image description hereCareful friends may find that in Jakarta Bean Validation 3.0 , there is not much change in 3.0 here , only the package name and namespace have been changed. In fact, it is still the implementation of Bean Validation 2.0 .
insert image description here
Bean Validation is the same as the JPA we learned a long time ago. It only provides specifications and does not provide specific implementations. Currently, the
data validation frameworks that implement Bean Validation specifications mainly include:

  • Hibernate Validator
  • Apache BVal
    may have friends who will say that Hibernate is just an old-fashioned ORM framework? Isn't it used by no one now? In fact, Hibernate is under the slogan of Everything data , and it also provides Hibernate Search, Hibernate OGM and other solutions. Hibernate is only used less in China, and the semi-orm framework like mybatis is mainly used in China. We can take a look at google trends:

Search popularity of mybatis, jpa, Hibernate in China:
insert image description here
Search popularity of mybatis, jpa, Hibernate in the world:
I don't know why the picture can't be put in, let's take a small picture

Due to the domestic development environment, it can be said that 99.99% of developers must be using spring, and it happens that Spring Validation provides built-in encapsulation support for Bean Validation. You can use the @Validated annotation to implement declarative validation without directly calling Bean Validation. Provided API methods. In terms of implementation principle, it is also based on Spring AOP interception, and finally calls different implementation frameworks of Bean Validation. For example, Hibernate Validator. The realization of verification-related operations is similar to Spring Transaction transactions, and declarative transactions are realized through the @Transactional annotation. Next, let's start learning how to implement parameter verification in Spring Boot.

2. Notes

Before getting started, let's understand the annotations that may be involved in this article. Under the javax.validation.constraints package, a series of constraint ( constraint ) annotations are defined. A total of 22, as follows:
insert image description here
can be roughly divided into the following categories:

2.1 Null and non-null checks

  • @NotBlank: It can only be used when the string is not null, and the length of the string after #trim() must be greater than 0.
  • @NotEmpty: The elements of the collection object are not 0, that is, the collection is not empty, and it can also be used when the string is not null.
  • @NotNull: Cannot be null.
  • @Null : Must be null.

2.2 Numerical check

  • @DecimalMax(value) : 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 : Determine positive numbers.
  • @PositiveOrZero: Judge positive or 0.
  • @Max(value) : The value of the field can only be less than or equal to this value.
  • @Min(value) : The value of the field can only be greater than or equal to this value.
  • @Negative : Judge negative numbers.
  • @NegativeOrZero: Judge negative 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 string, array, collection, Map, etc.

2.5 Date check

  • @Future : The annotated element must be a future date.
  • @FutureOrPresent : Determine whether the date is a future or present date.
  • @Past : Checks that the field's date is in the past.
  • @PastOrPresent : Determine whether the date is a past or present date.

2.6 Other checks

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

2.7 Additional Constraint Annotations for Hibernate Validator

Under the org.hibernate.validator.constraints package, a series of constraint ( 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 annotated string must be a valid URL.
  • @SafeHtml : Determine whether the submitted HTML is safe. For example, it cannot contain javascript scripts and so on.

insert image description here

2.8 @Valid 和 @Validated

The @Valid annotation is defined by Bean Validation and can be added to ordinary methods, constructors, method parameters, method returns, and member variables to indicate that they need to be checked for constraints.

The @Validated annotation is a Spring Validation lock definition that can be added to classes, method parameters, and ordinary methods to indicate that they need to be checked for constraints. At the same time, @Validated has a value attribute, which 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 is easy to confuse @Valid (under the javax.validation package) and @Validated (under the org.springframework.validation.annotation package) annotations. The two generally have the following differences:

name Does spring Validation implement declarative validation Whether to support nested validation Whether to support group verification
@Validated yes no yes
@Valid no yes no

@Valid has a nested object validation function. For example, if the @Valid annotation is not added to the User.profile property, it will cause the UserProfile.nickname property to not be 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 scenarios, we can use the @Validated annotation. In scenarios with nested validation, we use the @Valid annotation to add to member properties.

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 system, spring-boot-starter-validation dependency is also provided. Here, we did not introduce. why? Because spring-boot-starter-web has introduced spring-boot-starter-validation, and spring-boot-starter-validation has also introduced hibernate-validator dependencies, so there is no need to repeat the introduction. The dependency import relationship of the three can be seen in the figure below
insert image description here

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]+$", message = "账号格式为数字以及字母")
    private String username;
    /**
     * 密码
     */
    @NotEmpty(message = "密码不能为空")
    @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
    private String password;
}

UserController is used to write interfaces. On the class, add the @Validated annotation to indicate that all interfaces of UserController need parameter validation.

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 we can enter in the browser: swagger access address : http://localhost:8080/doc.html#/home Open the swagger document to test:

insert image description here
First, we visit http://localhost:8080/users/get?id=-1 to test, check the returned results, and check our id.
insert image description here
Next, we visit http://localhost:8080/users/add to verify the new user: we write the request body as:

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

Then return the result 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 the parameter error detail array. Each array element corresponds to a parameter error detail. Here, username violates the account length of 5-16 characters. password violates the password length of 4-16 characters.
Schematic diagram of returned results:
insert image description here

3.3 Some questions

Careful friends here may have a few questions:

3.3.1 Question 1

On the #get(id) method, we did not add the @Valid annotation to the id, and on the #add(addDTO) method, we added the @Valid annotation to the addDTO. Why is this difference?

Because UserController uses the @Validated annotation, Spring Validation will use AOP for aspect and parameter validation. The interceptor of this aspect uses MethodValidationInterceptor .

  • For the #get(id) method, the parameter id that needs to be verified is spread out, so there is no need to add the @Valid annotation.
  • For the #add(addDTO) method, the parameter addDTO that needs to be verified is actually equivalent to nested verification. The parameters to be verified are all in addDTO, so you need to add @Valid (in fact, adding @Validated is also OK for actual measurement, not for now. If you know why, just use @Valid for better distinction) annotation.

3.3.2 Question 2

The result returned by the #get(id) method is status = 500, and the result returned by the #add(addDTO) method is status = 400.

  • For the #get(id) method, in the MethodValidationInterceptor interceptor, if the parameter is verified to be incorrect, a ConstraintViolationException will be thrown.
  • For the #add(addDTO) method, because addDTO is a POJO object, it will use the SpringMVC DataBindermechanism , and it will call DataBinder#validate(Object... validationHints)the method for verification. It will be thrown when the verification fails BindException .

In SpringMVC, exceptions are DefaultHandlerExceptionResolverhandled .

  • For BindExceptionexceptions , it is processed as 400the status code of .
  • For ConstraintViolationExceptionthe exception , there is no special handling, so it is processed as 500the status code of .
    Here, we are throwing a question. If the #add(addDTO method, if the parameters are correct, will it go through the MethodValidationInterceptor interceptor after going through the parameter verification in DataBinder ? Think about 100 milliseconds...

The answer is yes. This leads to waste. So in the Controller class, if there is 只有a similar #add(addDTO) method 嵌套校验, then I don't need to add the @Validated annotation to the Controller class. In this way, only DataBinder is used for parameter verification.

3.3.3 The return prompt is very unfriendly and too long

The third point, whether it is the #get(id) method or the #add(addDTO) method, their return prompts are very unfriendly, so what should we do? We'll 处理校验异常deal .

4. Handle validation exceptions

4.1 Enumeration class that fails the verification

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 class

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 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

Visit: 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.
insert image description here
insert image description here

Guess you like

Origin blog.csdn.net/weter_drop/article/details/130046637