一、引入swagger的目的
相信看这篇文章的小伙伴已经知道它的好处了,此次就不多说了,还不清楚的找谷歌、百度。
二、本文借用Swagger要实现的效果
1.后端人员在代码里根据swagger语法打些标签,生成可预览的Api文档,方便前端及app人员开发,无须再维护一份Word文档。
2.Swagger文档token机制的优化
3.分组显示api
4.最核心、最重要的一点:让Swagger支持自定义注解。
三、功能实现
1.添加依赖
spring-boot版本:1.5.7;swagger2版本:2.7.0
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
2.创建swagger2配置类
@Configuration
@EnableSwagger2
//@Profile({"dev", "test"})//在生产环境不开启,此方式有问题,待查找资料解决,故用下面设置布尔值来实现安全控制
public class Swagger2 {
@Value("${swagger.show}")
private boolean swaggerShow;
/**
* 全局设置Content Type,默认是application/json
* 如果想只针对某个方法,则注释掉改语句,在特定的方法加上下面信息
* @ApiOperation(consumes="application/x-www-form-urlencoded")
*/
public static final HashSet<String> consumes = new HashSet<String>() {{
add("application/x-www-form-urlencoded");
}};
/**实现点:业务系统的token认证机制**/
public List getTokenPar(){
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<Parameter>();
tokenPar.name("Authorization")
.description("认证信息")
.modelRef(new ModelRef("string")).parameterType("header").required(true).build();
pars.add(tokenPar.build());
return pars;
}
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.enable(this.swaggerShow)//是否开启swagger
.groupName("例子")
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.zxy.product.training.web.example"))//扫描包
.paths(
())
.build()
.globalOperationParameters(getTokenPar())
.consumes(Swagger2.consumes);
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("例子模块")//标题
.description("例子模块接口调试")
.contact("cenjiajun的简书")
.version("1.0")
.build();
}
/**实现点:分组显示api**/
private Predicate<String> examplePaths() {
return PathSelectors.regex("/cjj-example.*");
}
}
如上,已经实现了前三点功能:生成api文档,分组显示api,业务系统token认证。
3.实现swagger支持自定义注解
@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 10000)
public class SwaggerRequestConditionReader extends AbstractOperationParameterRequestConditionReader {
@Autowired
private TypeResolver paramTypeResolver;
public SwaggerRequestConditionReader(TypeResolver resolver) {
super(resolver);
}
@Override
public void apply(OperationContext context) {
// @Params为业务系统自定义注解,主要是做入参校验跟swagger2原生标签@ApiImplicitParams类似
Optional<Params> paramsAnnotation = context.findAnnotation(Params.class);
// @Param为业务系统自定义注解,主要是做入参校验跟swagger2原生标签@ApiImplicitParam类似
Optional<Param> paramAnnotation = context.findAnnotation(Param.class);
// 获取标签上的属性
if (paramsAnnotation.isPresent()) {
Params params = paramsAnnotation.get();
List<Parameter> parameters = Arrays.stream(params.value()).map(param -> {
Parameter parameter = createParameter(param);
return parameter;
}).collect(Collectors.toList());
context.operationBuilder().parameters(parameters);
}
if (paramAnnotation.isPresent()) {
Param param = paramAnnotation.get();
List<Parameter> parameters = newArrayList();
parameters.add(createParameter(param));
context.operationBuilder().parameters(parameters);
}
}
private Parameter createParameter(Param param) {
Parameter parameter = (new ParameterBuilder())
.name(param.name())
.description(param.value())
.defaultValue(param.defaultValue())
.required(param.required())
.type(paramTypeResolver.resolve(param.type()))
.modelRef(new ModelRef(param.type().getSimpleName().equals(Integer.class.getSimpleName())?int.class.getSimpleName():param.type().getSimpleName()))//解决无法识别Integer问题
.parameterType(StringUtils.isEmpty(param.paramType())?"query":param.paramType()).build();//如果不传paramType字段取默认值
return parameter;
}
}
4.在方法上使用自定义标签
@ApiOperation(value = "xxx数据请求接口", notes = "xx请求数据接口,用于判断当前是否允许报名等")
@RequestMapping(value = "/front-check/{id}", method = RequestMethod.GET)
@Permitted
@Param(name = "id", required = true, value="班级id", paramType = "path",type = String.class)
@JSON("xx,xx,xx,xx,xx")
@JSON("classStudent.(id,xx,status,xx)")
public ClassInfo getForRegisterCheck(RequestContext requestContext, Subject<Member> subject) {
return classInfoService.getForRegisterCheck(requestContext.getString("id"), subject.getCurrentUserId());
}
说明:swagger2默认的contenttype为json,上面配置类我已改成表单格式。 @Param里的name属性就是表单上的key,required表示该字段是否为必填项,value就是对属性的解释说明,type说明了该字段的数据类型,而paramType = "path"用来获取/{xx}参数,其它情况下用query即可,我已改成默认,详情见第三点
四、API文档访问与调试
访问方式:ip地址+:8010/api/v1/xx(如果配置了代理,就无需这段)+/swagger-ui.htm。如:http://192.168.0.145:8010/api/v1/xx/swagger-ui.html
五、安全问题
系统上生产环境时应当禁止访问swagger,防止被攻击,所以该插件只适用于开发和测试环境。目前有两种灵活切换方式,其中有一种存在bug,待寻求解决方案。
方案一:配置文件的多环境配置(有问题,暂时不用)
application-pro.properties
application-dev.properties
application-test.properties
application.properties
在application.properties加入以下配置:
spring.profiles.active=dev或者test或者pro
方案二:在配置文件给一个布尔值
swagger.show=true
最后:如果想通过命令修改配置文件信息,可这样操作java -jar xxx.jar --spring.profiles.active=dev java -jar xxx.jar --swagger.show=false。不过采用微服务架构一般都会配套使用自动发版工具,此时就只能配置个环境变量了。