异常统一处理:BindException(绑定异常)
作者:mmseoamin日期:2024-02-06

概述

在Spring Boot应用中,数据绑定是一个至关重要的环节,它负责将HTTP请求中的参数映射到控制器方法的入参对象上。在这个过程中如果遇到问题,如参数缺失、类型不匹配或验证失败等,Spring MVC将会抛出一个org.springframework.validation.BindException异常。本文将深入解析BindException异常的原因和处理方式。

  • 本文仅详细解析 BindException 的异常处理;
  • 关于 全局异常统一处理 的原理和完整实现逻辑,请参考文章:《SpringBoot 全局异常统一处理(AOP):@RestControllerAdvice + @ExceptionHandler + @ResponseStatus》

数据绑定异常的来源

BindException通常与数据绑定和参数校验紧密相关。当用户通过HTTP请求向服务器发送数据时,Spring MVC框架会尝试自动将这些请求参数绑定到控制器方法的入参对象上。如果在这个过程中发生以下情况,就可能引发BindException异常:

  1. 参数映射错误:请求中的参数未能正确地映射到目标对象的属性上。
  2. 注解校验失败:对象属性使用了javax.validation或者org.springframework.validation包下的注解进行校验(如@NotNull、@Size等),而传入的值不符合这些注解所定义的约束条件。

具体应用场景示例

本段所说的 请求参数,均指的是控制器方法的入参对象的属性。

  • 必填字段为空(或空白):若某个字符串类型的字段标记为@NotBlank,但未接收到对应的参数或参数为空白(不包含非空格的字符),则会触发BindException异常。@NotNull、@NotEmpty 的逻辑类似,校验失败也会触发BindException异常。
  • 数据格式不匹配:例如,尝试将请求参数的字符串值转换为目标类型(如整数或日期)时无法成功转换。
  • 字段长度超出限制:对于有最大或最小长度限制的字段,当请求参数的长度超过这些限制时,会引发异常。
  • 正则表达式不匹配:当字段的值需满足特定的正则表达式模式,而实际提供的参数不满足该模式时,也会导致异常。
  • 自定义验证逻辑失败:通过@Valid注解配合自定义验证器进行参数校验时,如果验证失败,同样会抛出BindException异常。

    异常处理代码

    核心代码

    在Spring Boot应用中,我们可以利用@ExceptionHandler注解来捕获并处理BindException异常,如下所示的代码片段提供了一种通用的异常处理策略:

        /**
         * 参数校验异常:对象参数校验。
         */
        @ExceptionHandler
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        public Result handleException(BindException e, HandlerMethod handlerMethod) {
            logInfo(e, handlerMethod);
            List fieldErrors = e.getFieldErrors();
            String userMessage = UserTipGenerator.getUserMessage(fieldErrors);
            String errorMessageCore = ErrorMessageGenerator.getErrorMessage(fieldErrors);
            String errorMessage = String.format("【参数校验异常】(错误数量:%s):%s", e.getErrorCount(), errorMessageCore);
            return Result.fail(userMessage, String.valueOf(HttpStatus.BAD_REQUEST.value()), errorMessage);
        }
    

    上述代码中,当出现BindException异常时,系统将返回一个状态码为400(Bad Request)的结果,并附带详细的错误信息,包括哪个字段校验失败以及失败原因。

    相关代码:UserTipGenerator,ErrorMessageGenerator

    异常统一处理:BindException(绑定异常),在这里插入图片描述,第1张

    异常统一处理:BindException(绑定异常),在这里插入图片描述,第2张

    详细代码,请参考后面的参考文章《SpringBoot 全局异常统一处理(AOP):@RestControllerAdvice + @ExceptionHandler + @ResponseStatus》

    测试案例

    1. 必填字段为空

    测试代码

    假设我们有一个新增用户的接口,其中入参要求不能为空:

        @PostMapping("users")
        @Operation(summary = "新增用户")
        public void addUser(@Valid @RequestBody UserAddParam param) {
            log.info("测试:新增用户,Post请求。param={}", param);
        }
    
    package com.example.web.response.model.param;
    import io.swagger.v3.oas.annotations.media.Schema;
    import lombok.Data;
    import javax.validation.constraints.NotBlank;
    @Data
    @Schema(name = "新增用户Param")
    public class UserAddParam {
        @NotBlank(message = "姓名,不能为空")
        @Schema(description = "姓名", example = "张三")
        private String name;
        
        // ...
    }
    

    异常统一处理:BindException(绑定异常),在这里插入图片描述,第3张

    未处理时情况

    • 请求响应

      异常统一处理:BindException(绑定异常),在这里插入图片描述,第4张

      • 控制台的错误日志

        异常统一处理:BindException(绑定异常),在这里插入图片描述,第5张

        处理后接口请求响应

        异常统一处理:BindException(绑定异常),在这里插入图片描述,第6张

        2. 数据格式不匹配

        一下两种情况,会导致出现数据格式异常:

        1. 输入无法成功转换指定的数字类型。比如,入参的数据类型为整数Integer,但输入为:[张三] 。
        2. 输入的值超过允许的最大值。比如,入参的数据类型为整数Integer,但输入为:[1234567890123456789],而Integer的最大值为[231-1,即 2147483647] ;此时内嵌的异常为 NumberFormatException 。

        测试代码

            @GetMapping(path = "users")
            @Operation(summary = "查询用户列表", description = "测试:BindException。参数校验异常:Get请求,Query参数,以对象的形式接收。")
            public List listUsers(@Valid UserQuery userQuery, PageQuery pageQuery,
                                          HttpServletRequest request, HttpServletResponse response, HttpSession session) {
                log.info("查询用户列表。userQuery={},pageQuery={}", userQuery, pageQuery);
                // 业务代码...
            }
        
        package com.example.core.model;
        import io.swagger.v3.oas.annotations.media.Schema;
        import lombok.Data;
        import org.springdoc.api.annotations.ParameterObject;
        @Data
        @ParameterObject
        @Schema(name = "分页参数Query")
        public class PageQuery {
            @Schema(description = "当前页码", type = "Integer", defaultValue = "1", example = "1", minimum = "1")
            private Integer pageNumber = 1;
            @Schema(description = "每 1 页的数据量", type = "Integer", defaultValue = "10", example = "10", minimum = "1", maximum = "100")
            private Integer pageSize = 10;
        }
        

        未处理的情况

        异常统一处理:BindException(绑定异常),在这里插入图片描述,第7张

        异常统一处理:BindException(绑定异常),在这里插入图片描述,第8张

        异常统一处理:BindException(绑定异常),在这里插入图片描述,第9张

        异常统一处理:BindException(绑定异常),在这里插入图片描述,第10张

        处理后接口请求响应

        异常统一处理:BindException(绑定异常),在这里插入图片描述,第11张

        异常统一处理:BindException(绑定异常),在这里插入图片描述,第12张

        3. 自定义验证逻辑失败

        参考相关专栏:《SpringBoot - 接口参数校验》

        总结

        在没有处理BindException的情况下,如果客户端提交了缺少必要参数的请求,服务端将返回包含错误信息的标准HTTP响应,并在控制台打印详细的错误日志。而经过上述异常处理器处理后,客户端接收到的响应将以更友好的格式呈现错误详情,便于快速定位和修复问题。

        通过适当地处理BindException异常,不仅可以提升应用的健壮性,还能优化用户体验,使得API接口的错误反馈更加清晰明确。