简介

knife4j是swagger的增强版,更契合微服务架构,ui前身是swagger-bootstrap-ui,api注解的使用方式和swagger一致。

Spring boot整合knife4j

pom文件
<!-- 包含了ui界面 -->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <!-- 2.x基于springfox2.x,3.x基于springfox3.x-->
    <version>3.0.2</version>
</dependency>
Configuration类 跟 swagger配置一样
@Configuration
@EnableSwagger2
@EnableKnife4j
@Import(BeanValidatorPluginsConfiguration.class)
public class Knife4jConfiguration {
    /**
     * 可指定多个Docket区分不同的分组
     *
     * @return
     */
    @Bean
    public Docket defaultApi() {
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                //分组名称
                .groupName("2.0.0版本")
                .select()
                //这里指定Controller扫描包路径 ,不能用 * 
                .apis(RequestHandlerSelectors.basePackage("com.*.*"))
                //添加Api注解才显示
//				.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }
 
 
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Api接口")
				.description("# daping's RESTful APIs")
                .termsOfServiceUrl("***")
                .version("1.0.0")
                .build();
    }
}

####注解使用
在controller接口上添加对应的注解即可

@Api(tags = "登录")
@RestController
@RequestMapping("/oauth")
@AllArgsConstructor
@Slf4j
public class LogoutController {

    @DeleteMapping("/login")
    public Result login() {
        return Result.success();
    }

}
访问路径

项目路径+/doc.html

Spring Cloud集成knife4j

本文只介绍Spring Cloud GateWay集成knife4j。

pom文件
<!-- 包含了ui界面 -->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <!-- 2.x基于springfox2.x,3.x基于springfox3.x-->
    <version>3.0.2</version>
</dependency>
yml文件配置
spring
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 启用服务发现
          lower-case-service-id: true
      routes:
        - id: mall-auth
          uri: lb://mall-auth
          predicates:
            - Path=/mall-auth/**
          filters:
            - SwaggerHeaderFilter
            - StripPrefix=1
网关配置文件

SwaggerHeaderFilter 配置
在集成Spring Cloud Gateway网关的时候,会出现没有basePath的情况(即定义的例如/user、/order等微服务的前缀),这个情况在使用zuul网关的时候不会出现此问题,因此,在Gateway网关需要添加一个Filter实体Bean

@Component
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
    private static final String HEADER_NAME = "X-Forwarded-Prefix";
    private static final String URI = "/v2/api-docs";
    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getPath();
            if (!StringUtils.endsWithIgnoreCase(path,URI )) {
                return chain.filter(exchange);
            }
            String basePath = path.substring(0, path.lastIndexOf(URI));
            ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
            ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
            return chain.filter(newExchange);
        };
    }
}

SwaggerResourceConfig和SwaggerHandler
在我们使用Spring Boot等单体架构集成swagger项目时,是通过对包路径进行业务分组,然后在前端进行不同模块的展示,而在微服务架构下,我们的一个服务就类似于原来我们写的一个业务组

springfox-swagger提供的分组接口是swagger-resource,返回的是分组接口名称、地址等信息

在Spring Cloud微服务架构下,我们需要重写该接口,主要是通过网关的注册中心动态发现所有的微服务文档,代码如下:

@Slf4j
@Component
@Primary
@AllArgsConstructor
public class SwaggerResourceConfig implements SwaggerResourcesProvider {
    private final RouteLocator routeLocator;
    private final GatewayProperties gatewayProperties;
    @Override
    public List<SwaggerResource> get() {
        List<SwaggerResource> resources = new ArrayList<>();
        List<String> routes = new ArrayList<>();
        routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
        gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route -> {
            route.getPredicates().stream() .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
                    .forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(),
                            predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0") .replace("**", "v2/api-docs"))));
        });
        return resources;
    }
    private SwaggerResource swaggerResource(String name, String location) {
        log.info("name:{},location:{}",name,location);
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location);
        swaggerResource.setSwaggerVersion("2.0");
        return swaggerResource;
    }
}
@RestController
public class SwaggerHandler {

    @Autowired(required = false)
    private SecurityConfiguration securityConfiguration;

    @Autowired(required = false)
    private UiConfiguration uiConfiguration;

    private final SwaggerResourcesProvider swaggerResources;

    @Autowired
    public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
        this.swaggerResources = swaggerResources;
    }


    @GetMapping("/swagger-resources/configuration/security")
    public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    @GetMapping("/swagger-resources/configuration/ui")
    public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    @GetMapping("/swagger-resources")
    public Mono<ResponseEntity> swaggerResources() {
        return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }
}
其他微服务使用
@EnableKnife4j
@Configuration
@EnableSwagger2WebMvc
public class SwaggerConfiguration {

    @Bean
    public Docket restApi() {
        //schema
        List<GrantType> grantTypes=new ArrayList<>();
        //密码模式
        String passwordTokenUrl="http://localhost:9999/mall-auth/oauth/token";
        ResourceOwnerPasswordCredentialsGrant resourceOwnerPasswordCredentialsGrant=new ResourceOwnerPasswordCredentialsGrant(passwordTokenUrl);
        grantTypes.add(resourceOwnerPasswordCredentialsGrant);
        OAuth oAuth=new OAuthBuilder().name("oauth2")
                .grantTypes(grantTypes).build();
        //context
        //scope方位
        List<AuthorizationScope> scopes=new ArrayList<>();
        scopes.add(new AuthorizationScope("read","read  resources"));
        scopes.add(new AuthorizationScope("write","write resources"));
        scopes.add(new AuthorizationScope("reads","read all resources"));
        scopes.add(new AuthorizationScope("writes","write all resources"));

        SecurityReference securityReference=new SecurityReference("oauth2",scopes.toArray(new AuthorizationScope[]{}));
        SecurityContext securityContext=new SecurityContext(Lists.newArrayList(securityReference),PathSelectors.ant("/**"));
        //schemas
        List<SecurityScheme> securitySchemes= Lists.newArrayList(oAuth);
        //securyContext
        List<SecurityContext> securityContexts= Lists.newArrayList(securityContext);
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.danir.auth.controller"))
                .paths(PathSelectors.any())
                .build()
                .securityContexts(securityContexts)
                .securitySchemes(securitySchemes)
                .apiInfo(apiInfo());
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title("OAuth2认证中心")
                .description("<div style='font-size:14px;color:red;'>OAuth2认证、注销、获取验签公钥接口</div>")
                .termsOfServiceUrl("https://www.danir.store")
                .contact(new Contact("yl", "https://github.com/dnir", "994242104@qq.com"))
                .license("Open Source")
                .licenseUrl("https://www.apache.org/licenses/LICENSE-2.0")
                .version("1.0.0")
                .build();
    }

}

参数详解

业务相关类中如果想要显示接口在在线文档里,需要加入相关注解。这里注解同swagger类似。

  • @Api:用在请求的类上,表示对类的说明 tags=“说明该类的作用,可以在UI界面上看到的注解”
    value=“该参数没什么意义,在UI界面上也看到,所以不需要配置”
  • @ApiOperation:用在请求的方法上,说明方法的用途、作用 value=“说明方法的用途、作用” notes=“方法的备注说明”
  • @ApiImplicitParams:用在请求的方法上,表示一组参数说明
  • @ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面 name:参数名
    value:参数的汉字说明、解释 required:参数是否必须传 paramType:参数放在哪个地方 · header -->
    请求参数的获取:@RequestHeader · query --> 请求参数的获取:@RequestParam ·
    path(用于restful接口)–> 请求参数的获取:@PathVariable · body(不常用) · form(不常用)
    dataType:参数类型,默认String,其它值dataType=“Integer” defaultValue:参数的默认值
  • @ApiResponses:用在请求的方法上,表示一组响应
  • @ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息 code:数字,例如400
    message:信息,例如"请求参数没填好" response:抛出异常的类
  • @ApiModel:用于响应类上,表示一个返回响应数据的信息 (这种一般用在post创建的时候,使用@RequestBody这样的场景,
    请求参数无法使用@ApiImplicitParam注解进行描述的时候)
  • @ApiModelProperty:用在属性上,描述响应类的属性
配置效果如下

在这里插入图片描述

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐