spring boot项目集成knife4j 2.0.5并实现入参分组校验显示
之前写过一篇:前后端分离时如何优雅的编写API文档
不过其中的部分配置还不够完善,本次对其进行一定的优化。
1 路径分组配置
项目中,有的路径需要登录,有的不需要登录,需要登录的接口还可能需要配置全局header,用于传输校验使用的token等
这里是使用路径进行是否需要登录的匹配,其中,路径包含/pub,则不需要登录,否则,需要登录,同时扫描多个路径,路径之前用英文逗号(,)隔开即可
具体实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| @Autowired private SwaggerProperties properties; @Bean(value = "defaultApi") public Docket defaultApi() { return new Docket(DocumentationType.SWAGGER_2) .useDefaultResponseMessages(false) .apiInfo(apiInfo()) .groupName(properties.getGroupName() + "-待认证") .select() .apis(getPredicate()) .paths(PathSelectors.regex("^((?!pub).)*$")) .build() .securitySchemes(securitySchemes()) .securityContexts(securityContexts()); }
@Bean(value = "pubApi") public Docket pubApi() { return new Docket(DocumentationType.SWAGGER_2) .useDefaultResponseMessages(false) .apiInfo(apiInfo()) .groupName(properties.getGroupName() + "-无认证") .select() .apis(getPredicate()) .paths(PathSelectors.regex("^.*pub.*$")) .build(); } private Predicate getPredicate() { String[] split = properties.getBasePackage().split(","); Predicate predicate = null; for (String s : split) { Predicate secPredicate = RequestHandlerSelectors.basePackage(s); predicate = predicate == null ? secPredicate : Predicates.or(secPredicate, predicate); } return predicate; }
private List<ApiKey> securitySchemes() { return newArrayList( new ApiKey(properties.getHeader(), properties.getHeader(), "header")); }
private List<SecurityContext> securityContexts() { return newArrayList( SecurityContext.builder() .securityReferences(defaultAuth()) .forPaths(PathSelectors.regex("^((?!pub).)*$")) .build() ); }
private List<SecurityReference> defaultAuth() { AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; authorizationScopes[0] = authorizationScope; return newArrayList( new SecurityReference(properties.getHeader(), authorizationScopes)); }
|
SwaggerProperties 配置文件只是把变化的配置放到了配置文件中进行配置
这样,访问http://{IP}:{port}/doc.html 地址,即可看到效果
2 访问控制
knife提供了简单的basic认证,其中
登录访问:SecurityBasicAuthFilter、生产环境屏蔽:ProductionSecurityFilter
由于项目自定义了部分配置,为了统一,自己实现注入时机
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Bean @ConditionalOnMissingBean(SecurityBasicAuthFilter.class) @ConditionalOnProperty(name = "swagger.certifiable", havingValue = "true") public SecurityBasicAuthFilter securityBasicAuthFilter(SwaggerProperties properties) { return new SecurityBasicAuthFilter(properties.getCertifiable(), properties.getUsername(), properties.getPassword()); }
@Bean @ConditionalOnMissingBean(ProductionSecurityFilter.class) @ConditionalOnProperty(name = "swagger.prod", havingValue = "true") public ProductionSecurityFilter productionSecurityFilter(SwaggerProperties properties) { return new ProductionSecurityFilter(properties.getProd()); }
|
这样,在SwaggerProperties中配置相应的信息,即可实现相关功能
3 入参分组
在开发的时候,会遇到这样一个情况:
一个VO对象在新增的时候某些字段为必填,在更新的时候又非必填。比如用户新增和编辑功能,新增时id不是必填,编辑时是必须的,其他的字段如用户名等都是一样新增、编辑都为必填。
而在swagger中,不属于新增接口的id字段,就不应该显示到文档上,避免前端集成接口不知如何传参的问题
以前是新增、编辑使用不同的实体,或者编辑继承新增实体,再加上id字段,但是这样会造成项目中有很多的实体,类臃肿
基于此需求,可以借助 javax.validation.groups 分组功能以及@Validated 来实现,同时扩展swagger的功能,此处参考:https://blog.csdn.net/jianzhang11/article/details/119632467
3.1 自定义分组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import javax.validation.groups.Default;
public interface ValidGroup extends Default { interface Create extends ValidGroup { }
interface Update extends ValidGroup { }
interface Query extends ValidGroup { }
interface Delete extends ValidGroup { } }
|
3.2 使用
controller层入参上添加分组信息,如:
新增:ValidGroup.Create.class
1 2 3
| public RestResult<String> insertUser(@RequestBody @Validated(ValidGroup.Create.class) UserBO user) { return usersService.insertUser(user); }
|
编辑:ValidGroup.Update.class
1 2 3
| public RestResult<String> updateUser(@RequestBody @Validated(ValidGroup.Update.class) UserBO user) { return usersService.updateUser(user); }
|
入参实体中:
1 2 3 4 5 6 7 8
| public class UserBO implements Serializable { private static final long serialVersionUID = 5699245096095831445L;
@ApiModelProperty(value = "ID") @Null(groups = ValidGroup.Create.class) @NotNull(groups = ValidGroup.Update.class, message = "ID不可为空") private Long id; }
|
这样,再访问新增、编辑接口,即可看到效果。