Spring Boot 学习笔记 -- Swagger的使用笔记

目录

1.Swagger依赖导入和基本演示

2.Swagger中继承子类的配置

3.Swagger与拦截器联合使用注意事项

4.Swagger全局token配置和使用

5.Swagger分组进行token配置

6.Swagger多环境配置:开发和线上环境


1.Swagger依赖导入和基本演示

 首先我们先建立一个简单的例子,引入swagger相关的maven依赖,然后生成一个简单的spring boot测试工程,IDE当然是首选的Intelij idea。其中swagger相关依赖如下

        
        
            io.springfox
            springfox-swagger2
            2.8.0
        
        
            io.springfox
            springfox-swagger-ui
            2.8.0
        
        

把工程先建好,我这里偷懒了,使用了Easy Code的插件来完成,我记录了部分步骤以及说明,放上长图如下。

OK,借助插件我们的演示工程就快速的搭建起来了,下面进入正题,看下Swagger的配置文件,就是工程中我手动新增的config文件夹。

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket createDocket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.cjt.swaggerdemo"))
                .paths(PathSelectors.any())
                .build();
    }

    /**
     * 定义api信息
     * @return
     */
    private ApiInfo apiInfo(){
        return new ApiInfoBuilder()
                //页面标题
                .title("XXX平台API")
                //创建人
                .contact(new Contact("Cao Jiangtao", "https://blog.csdn.net/u010898329", "[email protected]"))
                //版本号
                .version("1.0.0")
                .build();
    }

}

 

要想项目成功运行起来,要注意以下几点

  • EasyCode生成的dao要加上@Mapper注解,同时Application要加上@MapperScan注解。
  • 在pom中要引入mysql和mybatis依赖,同时配置文件中记得把数据库连接信息加上,不然会报错。
  • 上面的config中,Docket建立中apiInfo是非必须的,但是apis这项最最好补充完整,例如我上面是扫描相应的包路径。

看下Controller中的配置

/**
 * (TbStudent)表控制层
 *
 * @author Cao Jiangtao
 * @since 2020-04-10 16:49:02
 */
@RestController
@RequestMapping(value = "/student", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@Api(description = "学生表操作接口",tags = "学生表操作接口",produces = MediaType.APPLICATION_JSON_UTF8_VALUE,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class TbStudentController {
    /**
     * 服务对象
     */
    @Resource
    private TbStudentService tbStudentService;

    /**
     * 通过主键查询单条数据
     *
     * @param id 主键
     * @return 单条数据
     */
    @GetMapping("/selectOne")
    @ApiOperation(value = "通过主键查询单条学生数据")
    public TbStudent selectOne(String id) {
        return this.tbStudentService.queryById(id);
    }

}

 注意swagger的几个基本注解作用对象。

  • @Api对应类级别,一般放在Controller层。
  • @ApiOpration对应方法级别,也就是Controller中要对外暴露的接口方法。
  • @ApiModel对应是实体。
  • @ApiModelProperty对应的是实体中的字段。

具体大家可以去网上搜索看看别人的介绍。Swagger常用注解使用详解

看下运行的效果

Spring Boot 学习笔记 -- Swagger的使用笔记_第1张图片

到这里的话,Swagger与SpringBoot的整合和使用就基本结束了,下面我会讲讲几个不常用,但是比较有用,而且需要注意的场景和细节问题。

2.Swagger中继承子类的配置

在我们实际项目开发中,可能会遇到类似下面的情况,提出一个公共父类来给子类继承。

Spring Boot 学习笔记 -- Swagger的使用笔记_第2张图片

 这种场景其实也很常用,继承也是Java的三大特性之一,让每个子类看起来不那么的臃肿。

Spring Boot 学习笔记 -- Swagger的使用笔记_第3张图片

这里特别指出,@ApiModel注解中子类要指定父类 ,我之前遇到的问题是,没有指定父类,然后Swagger中打开的时候,父类中的所有属性在子类中均不展示,因此这是个要注意的细节。

关于@ApiModel的注解

  • value                 :为模型提供备用名称
  • description        :提供详细的类描述
  • parent                :为模型提供父类以允许描述继承关系
  • discriminatory   :支持模型继承和多态,使用鉴别器的字段的名称,可以断言需要使用哪个子类型
  • subTypes           :从此模型继承的子类型数组
  • reference           :指定对应类型定义的引用,覆盖指定的任何其他元数据

 

3.Swagger与拦截器联合使用注意事项

先说说我的需求:作者项目中有这样一个需求,拦截所有/api/**的请求,因为是对外提供接口,所以需要拦截验证。

这样的需求下,自然想到了要使用拦截器,但是在使用拦截器的时候遇到了问题:

Spring Boot 学习笔记 -- Swagger的使用笔记_第4张图片

Response body Unknown response type!!!

这个发生在拦截器拦截之后输出自己定义的错误提示的时候,只要请求都正常,过了拦截器之后,在controller层的自定义输出又都是正常的,遇到这个错误真是百思不得其解,卧槽了好久!!!但是我还得硬着头皮检查代码啊!!!最后还是被我解决了,原来我在拦截器中只定义了response返回体的编码格式为utf-8,但是没有定义返回体response的ContentType,最后把这句加上之后完美解决。

下面贴上我写的拦截器部分代码

public class TokenInterceptor implements HandlerInterceptor {

    Logger logger = LoggerFactory.getLogger(TokenInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.debug("-------------->>>>>>>>>>>>>>>>>>>>>>>>>>>>> ----- preHandle ------------->>>>>>>>>>>>>>>>>>>>>>>>>");
        Result result;
        response.setCharacterEncoding("utf-8");
        // 设置返回的数据格式
        response.setContentType("application/json;charset=UTF8");
        String accessToken = request.getHeader("x-access-token");
        if (null != accessToken) {
            try {
                Map map = JwtUtil.validateToken(accessToken);
                if (null != map) {
                    logger.debug("api map : {}", JSON.toJSONString(map));
                    // 解析出来的productKey传递到下一页
                    request.setAttribute("productKey", map.getOrDefault("productKey", null));
                    return true;
                } else {
                    result = ResultUtil.error(ResultEnum.TOKEN_IS_ERROR.getCode(),ResultEnum.TOKEN_IS_ERROR.getMsg(),null);
                }
            } catch (Exception e) {
                result = ResultUtil.error(ResultEnum.TOKEN_IS_ERROR.getCode(),ResultEnum.TOKEN_IS_ERROR.getMsg(),e.toString());
            }
        } else {
            result = ResultUtil.error(ResultEnum.TOKEN_IS_ERROR.getCode(),ResultEnum.TOKEN_IS_ERROR.getMsg(),null);
        }
        logger.debug("-------------->>>>>>>>>>>>>>>>>>>>>>>>>>>>> ----- accessToken : {}", accessToken);
        PrintWriter writer = response.getWriter();
        writer.write(JSON.toJSONString(result));
        writer.flush();
        writer.close();
        logger.debug("-------------->>>>>>>>>>>>>>>>>>>>>>>>>>>>> ----- baseResp : {}", JSON.toJSONString(result));
        return false;
    }

}

上面代码中的返回响应体的ContentType一定要设置

// 设置返回的数据格式
response.setContentType("application/json;charset=UTF8");

 关于Contenttype设置可参考网上类似博客,我贴出常用部分给大家参考。

MediaType,即是Internet Media Type,互联网媒体类型;也叫做MIME类型,在Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息。
类型格式:type/subtype(;parameter)?
type 主类型,任意的字符串,如text,如果是号代表所有;
subtype 子类型,任意的字符串,如html,如果是号代表所有;
parameter 可选,一些参数,如Accept请求头的q参数, Content-Type的 charset参数。

常见的媒体格式类型如下:

    text/html : HTML格式
    text/plain :纯文本格式      
    text/xml :  XML格式
    image/gif :gif图片格式    
    image/jpeg :jpg图片格式 
    image/png:png图片格式

以application开头的媒体格式类型:

   application/xhtml+xml :XHTML格式
   application/xml     : XML数据格式
   application/atom+xml  :Atom XML聚合格式    
   application/json    : JSON数据格式
   application/pdf       :pdf格式  
   application/msword  : Word文档格式
   application/octet-stream : 二进制流数据(如常见的文件下载)
   application/x-www-form-urlencoded : 
中默认的encType,form表单数据被编码为key/value格式发送到

4.Swagger全局token配置和使用

接着上面的需求继续说,token全局配置,这个网上有很多博客,当然笔者也尝试了下,贴出来我的使用过程。

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket createDocket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.cjt.swaggerdemo"))
//                .paths(PathSelectors.any())
                .paths(PathSelectors.regex("^(?!auth).*$"))
                .build()
                .securitySchemes(securitySchemes())
                .securityContexts(securityContexts());
    }

    /**
     * 定义api信息
     * @return
     */
    private ApiInfo apiInfo(){
        return new ApiInfoBuilder()
                //页面标题
                .title("XXX平台API")
                //创建人
                .contact(new Contact("Cao Jiangtao", "https://blog.csdn.net/u010898329", "[email protected]"))
                //版本号
                .version("1.0.0")
                .build();
    }

    private List securitySchemes() {
        List apiKeyList= new ArrayList();
        apiKeyList.add(new ApiKey("x-auth-token", "x-auth-token", "header"));
        return apiKeyList;
    }

    private List securityContexts() {
        List securityContexts=new ArrayList<>();
        securityContexts.add(
                SecurityContext.builder()
                        .securityReferences(defaultAuth())
                        .forPaths(PathSelectors.regex("^(?!auth).*$"))
                        .build());
        return securityContexts;
    }

    List defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        List securityReferences=new ArrayList<>();
        securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
        return securityReferences;
    }

}

按照网上的博客进行的配置,基本就是复制粘贴的,配置好之后确实出现了全局配置token的弹窗。

但是…………

不知道是我太菜了还是我太菜了,请求中我根本就拿不到这里配置的token值。

Spring Boot 学习笔记 -- Swagger的使用笔记_第5张图片

另外,我的接口不是所有的都要用携带token,比如获取token的接口就不需要token,这种配置方式对我来说也没有什么价值,所以果断弃用了这种方式。如果大家有兴趣使用这种方式,可以去深入研究下。

5.Swagger分组进行token配置

直接贴代码,主要是swagger的配置文件,注意如果是分组的话,么groupName这个属性一定要写,不然会有惊喜等着你

public class Swagger2 {

    @Bean
    public Docket createDeviceApi() {
        //添加head参数start
        ParameterBuilder tokenPar = new ParameterBuilder();
        List pars = new ArrayList<>();
        tokenPar.name("x-access-token").description("令牌").modelRef(new ModelRef("string")).parameterType("header").required(true).build();
        pars.add(tokenPar.build());

        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                //为当前包路径
                .apis(RequestHandlerSelectors.basePackage("com.cjt.demo.api.device"))
                .paths(PathSelectors.any())
                .build()
                .globalOperationParameters(pars)
                .groupName("操作接口-V1.0.0");
    }

    @Bean
    public Docket createOpenApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                //为当前包路径
                .apis(RequestHandlerSelectors.basePackage("com.cjt.demo.api.open"))
                .paths(PathSelectors.any())
                .build()
                .groupName("开放接口-V1.0.0");
    }

    //构建 api文档的详细信息函数,注意这里的注解引用的是哪个
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                //页面标题
                .title("XXXXAPI")
                //创建人
                .contact(new Contact("CaoJiangtao", "https://me.csdn.net/u010898329", "[email protected]"))
                //版本号
                .version("1.0.0")
                .build();
    }

}

注意我这里分了两组,一组开发接口,一组操作接口,而且操作接口中加入了token令牌,解决了上面开发接口不需要令牌的尴尬局面。看下展示效果,右上角会出现下拉选择框

Spring Boot 学习笔记 -- Swagger的使用笔记_第6张图片

 

6.Swagger多环境配置:开发和线上环境

实际项目中有很多多环境需求,可能在dev上需要展示swagger,但是生产prod上就要禁止了。那么swagger是否也支持多环境配置呢,答案是支持的,下面我们看下swagger多环境配置有两种方式。

  • 方式一:使用@Profile注解在SwaggerConfig类上

这个是spring的全局注解,根据指定的profile来加载配置文件

@Configuration
@EnableSwagger2
@Profile({"dev"}) // 注解是在开发环境的时候加载该类
public class Swagger2 {

    // 其他配置部分
    ……

}
  • 方式二:读取多环境配置文件状态值,使用 Docket.enable方式开启或关闭

    public class Swagger2 {
    
        /** Swagger开关配置 */
        @Value("${swagger.enable}")
        private boolean swaggerEnable;
    
        @Bean
        public Docket createOpenApi() {
            return new Docket(DocumentationType.SWAGGER_2)
                    .enable(swaggerEnable)
                    .apiInfo(apiInfo())
                    .select()
                    //为当前包路径
                    .apis(RequestHandlerSelectors.basePackage("com.cjt.demo.api.open"))
                    .paths(PathSelectors.any())
                    .build()
                    .groupName("开放接口-V1.0.0");
        }
    
        //构建 api文档的详细信息函数,注意这里的注解引用的是哪个
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    //页面标题
                    .title("XXXXAPI")
                    //创建人
                    .contact(new Contact("CaoJiangtao", "https://me.csdn.net/u010898329", "[email protected]"))
                    //版本号
                    .version("1.0.0")
                    .build();
        }
    
    }

     

在项目中呢,推荐使用方式二,因为我们一般会定制几个不同环境的application.yml配置文件,在不同的环境下,设置开关就OK了

 

 

你可能感兴趣的:(Spring,Boot学习笔记)