Day05-knife4j-Validation

203 阅读6分钟

Knife4j

关于Knife4j的常用注解:

  • @Api:添加在控制器类上,通过此注解的tags属性可配置模块名称,可以在每个模块名称之前添加数字,则API文档会根据此名称(名称前面的数字)升序排列,例如:

    @Api(tags = "01. 相册管理模块")
    
  • @ApiOperation:添加在处理请求的方法上,通过此注解的value属性可配置业务名称,例如:

    @ApiOperation("添加相册")
    
  • @ApiOperationSupport:添加在处理请求的方法上,通过此注解的order属性可配置各业务的排序,最终将按照order属性的值(int类型)升序排列,例如:

    @ApiOperationSupport(order = 100)
    
  • @ApiModelProperty:添加在POJO类型的属性上,通过此注解的value属性可配置请求参数的描述文本,通过此注解的required属性可配置是否必须提交此参数(注意:此配置并不具备检查功能),通过此注解的example属性可以配置示例值,例如:

    @ApiModelProperty(value = "相册名称", required = true)
    
  • @ApiImplicitParam:添加在处理请求的方法上,通过此注解的name属性表示你需要配置方法的哪个请求参数,通过此注解的value属性配置请求参数的描述文本,通过此注解的required属性可配置是否必须提交此参数,通过此注解的dataType属性配置请求参数的数据类型,通过此注解的example属性可以配置示例值,例如:

    @ApiImplicitParam(name = "id", value = "相册ID", required = true, dataType = "long")
    
  • @ApiImplicitParams:添加在处理请求的方法上,当存在多个请求参数的说明是通过@ApiImplicitParam进行配置时,应该将多个@ApiImplicitParam配置作为当前注解的参数值,例如:

    @ApiImplicitParams({
        @ApiImplicitParam(name = "id", value = "相册ID", required = true, dataType = "long"),
        @ApiImplicitParam(name = "username", value = "用户名", required = true)
    })
    

去除响应结果中的null

当需要“去除响应时为null的数据”时,可以在对应的属性上添加@JsonInclude注解进行配置,当注解属性取值为NON_NULL时,表示“不为null时,JSON结果中将包含此属性”,例如:

@Data
public class JsonResult implements Serializable {

    private Integer state;

    @JsonInclude(JsonInclude.Include.NON_NULL) // 重要
    private String message;
    
    // 暂不关心其它代码
    
}

也可以将注解配置在类上,则类中所有属性都会应用此注解的配置,例如:

@Data
@JsonInclude(JsonInclude.Include.NON_NULL) // 重要
public class JsonResult implements Serializable {

    private Integer state;
    private String message;
    
    // 暂不关心其它代码
    
}

如果项目中还有其它的类的对象会被控制器返回到客户端,当需要去除其它类的对象中为null的属性时,还需要在其它类上也添加相同的配置!或者,在配置文件中,通过spring.jackson.default-property-inclusion属性进行全局配置,例如:

image-20230224141853975.png

如果同时使用以上3种配置方式,将采取“范围越小越优先”的原则。

Spring Validation

关于Spring Validation

Spring Validation是用于检查参数的基本格式有效性的框架。

在项目中添加spring-boot-starter-validation依赖项:

<!-- Spring Boot支持Spring Validation的依赖项,用于检查参数的基本有效性 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

提示:在学习时,为了更清楚的了解执行过程,建议暂时不启用全局异常处理器!

检查POJO类型的参数

在需要被检查的参数前添加@Validated@Valid注解,表示需要检查此参数,例如:

@PostMapping("/add-new")
//                       ↓↓↓↓↓↓ 新增注解
public JsonResult addNew(@Valid AlbumAddNewDTO albumAddNewDTO) {
    // 
}

然后,在POJO类型的属性上使用“检查注解”(例如:@NotNull表示“不允许为null”)来配置检查规则,例如:

@Data
public class AlbumAddNewDTO implements Serializable {

    @NotNull // 新增注解
    private String name;
    
    // 暂不关心其它代码
    
}

当客户端提交的请求中不包含name属性时,服务器端将响应400错误!并且,在服务器端的控制台会有以下警告信息:

2023-02-24 14:42:16.018  WARN 22020 --- [nio-8080-exec-6] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors<EOL>Field error in object 'albumAddNewDTO' on field 'name': rejected value [null]; codes [NotNull.albumAddNewDTO.name,NotNull.name,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [albumAddNewDTO.name,name]; arguments []; default message [name]]; default message [不能为null]]

处理检查失败时的异常

当检查失败时,Spring Validation框架会抛出BindException,同时,此框架还有DefaultHandlerExceptionResolver对此异常进行处理,以至于默认的失败效果是响应400错误。

可以在全局异常处理器中添加处理BindException异常的方法:

@ExceptionHandler
public JsonResult handleBindException(BindException e) {
    String message = "请求参数格式错误!!!";
    return JsonResult.fail(ServiceCode.ERROR_BAD_REQUEST, message);
}

在处理异常时,应该对错误的原因进行更加精准的描述,此描述文本需要通过检查注解的message属性进行配置,例如:

@Data
public class AlbumAddNewDTO implements Serializable {

    @NotNull(message = "必须填写相册名称!") // 配置注解参数
    private String name;
    
    // 暂不关心其它代码
    
}

然后,在处理异常时,可以通过BindException对象的getFieldError().getDefaultMessage()获取此信息,例如:

@ExceptionHandler
public JsonResult handleBindException(BindException e) {
    String message = e.getFieldError().getDefaultMessage();
    return JsonResult.fail(ServiceCode.ERROR_BAD_REQUEST, message);
}

但是,当同时存在多种错误时,以上处理方式只能提示多种错误中的某1种,如果需要提示全部错误,需要调整为:

@ExceptionHandler
public JsonResult handleBindException(BindException e) {
    StringBuilder stringBuilder = new StringBuilder();
    List<FieldError> fieldErrors = e.getFieldErrors();
    for (FieldError fieldError : fieldErrors) {
        stringBuilder.append(fieldError.getDefaultMessage());
    }
    String message = stringBuilder.toString();

    return JsonResult.fail(ServiceCode.ERROR_BAD_REQUEST, message);
}

关于快速失败

快速失败表现为:当检查参数的格式不通过时,不再继续对其它参数的检查!

相关配置代码如下:

package cn.tedu.csmall.product.config;

import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.validation.Validation;

/**
 * Validation配置类
 *
 * @author java@tedu.cn
 * @version 0.0.1
 */
@Slf4j
@Configuration
public class ValidationConfiguration {

    public ValidationConfiguration() {
        log.debug("创建配置类对象:ValidationConfiguration");
    }

    @Bean
    public javax.validation.Validator validator() {
        return Validation.byProvider(HibernateValidator.class)
                .configure() // 开始配置
                .failFast(true) // 配置快速失败
                .buildValidatorFactory() // 构建Validator工厂
                .getValidator(); // 从Validator工厂中获取Validator对象
    }

}

检查简单类型的参数

当需要检查简单类型的参数时,需要先在当前类上添加@Validated注解,例如:

@RestController
@RequestMapping("/album")
@Validated // 新增
public class AlbumController {
    // 暂不关心
}

然后,在需要检查的参数上添加检查注解,例如:

@PostMapping("/delete")
//                                     ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 检查注解的配置
public JsonResult delete(@RequestParam @Range(min = 1) Long id) {
    albumService.delete(id);
    return JsonResult.ok();
}

提示:以上参数上的@RequestParam只是临时使用的,如果没有此注解,在API文档的调试界面中暂时无法显示参数的输入框。

当提交的请求参数id值小于1时,会响应500错误,并且在服务器端的控制台会提示错误,例如:

javax.validation.ConstraintViolationException: delete.id: 需要在19223372036854775807之间

则可以在全局异常处理器中添加处理以上异常的方法:

@ExceptionHandler
public JsonResult handleConstraintViolationException(ConstraintViolationException e) {
    StringBuilder stringBuilder = new StringBuilder();
    Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
    for (ConstraintViolation<?> constraintViolation : constraintViolations) {
        stringBuilder.append(constraintViolation.getMessage());
    }
    String message = stringBuilder.toString();
    return JsonResult.fail(ServiceCode.ERROR_BAD_REQUEST, message);
}

常用检查注解

使用Spring Validation框架时,常用的检查注解有:

  • @NotNull:不允许为null值,即客户端必须提交此参数

  • @NotEmpty:不允许为空值,即长度为0的字符串

    • 仅能作用于字符串类型的参数
  • @NotBlank:不允许为空白值,即仅由空格、TAB、换行符组成的字符串

    • 仅能作用于字符串类型的参数
  • @Pattern:通过此注解的regexp属性指定正则表达式,检查参数是否匹配此正则表达式

    • 仅能作用于字符串类型的参数
  • @Range:限制数值型的参数值的有效区间

    • 仅能作用于数值型的参数

以上2个注解可以同时添加在1个参数上。

以上@Range仅当参数值存在时执行检查,如果调用此方法时,传入的参数值为null,则此注解是无效的!所以,这类注解通常与@NotNull一起使用!

在源代码中,按住Ctrl键点击检查注解所在的包