美文网首页mvp
API文档生成之Springfox-Swagger框架的简单使用

API文档生成之Springfox-Swagger框架的简单使用

作者: KICHUN | 来源:发表于2018-06-22 22:21 被阅读0次

    后端开发一定厌倦了和前端对API接口的麻烦,之前还是小白时愣是手写word文档给前端工程师使用,各种copy字段加解释...

    然而,有更好用的API框架可以使用,可以让我们摆脱这种烦恼,下面说下spring-fox swagger的简单使用
    注:swagger是一种API规范,springfox是其规范的一种实现

    1. 引入swagger依赖

    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.8.0</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.8.0</version>
            </dependency>
    

    2. 配置swagger,本例中使用的是springboot配置,如需xml配置请自行百度

    编写SwaggerConfig
    添加注解
    @Configuration
    @EnableSwagger2
    Docket.apis()中配置的包名为要扫描生成API的Controller包名,将会为该包下的controller生成相关文档
    ApiInfo为相关信息,该信息将会展示在文档中

    /**
     * Created by wangqichang on 2018/6/22.
     */
    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
    
        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(apiInfo())
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.kichun.controller"))
                    .paths(PathSelectors.any())
                    .build();
        }
    
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title("炒鸡好用的系統API")
                    .description("更多内容请咨询开发者 王启昌")
                    .termsOfServiceUrl("http://kichun.xin/")
                    .contact("wangqichang")
                    .version("1.0")
                    .build();
        }
    }
    

    3. 在需要添加的controller类和方法中加上api注解

    加在类上
    @Api(value = "字典操作接口",tags = {"字典常量操作"})
    加在方法上
    @ApiOperation(value = "删除字典类型",notes = "")
    @ApiImplicitParam(name = "id",value = "字典ID",required = true,dataType = "String")

    /**
     *
     * @author wangqichang
     * @since 2018-06-15
     */
    @Api(value = "字典操作接口",tags = {"字典常量操作"})
    @RestController
    @RequestMapping("/dataDict")
    public class DataDictController {
        @Autowired
        private DataDictService dataDictService;
    
        @ApiOperation(value = "删除字典类型",notes = "")
        @ApiImplicitParam(name = "id",value = "字典ID",required = true,dataType = "String")
        @RequestMapping(value = "/delete", method = RequestMethod.POST)
        public ResultDTO delete(String id) {
    
            return null;
        }
    
    

    当有多个参数时用@ApiImplicitParams

        /**
         * 登录
         *
         * @author Qichang.Wang
         * @date 17:27 2018/6/14
         */
        @ApiOperation(value = "用户登录接口", notes = "")
        @ApiImplicitParams(value = { @ApiImplicitParam(name = "name", value = "用户名", required = true),
                @ApiImplicitParam(name = "pwd", value = "密码", required = true),
                @ApiImplicitParam(name = "rememberMe", value = "勾选时传递true", required = false) })
        @RequestMapping(value = "/login", method = RequestMethod.POST)
        public ResultDTO login(String name, String pwd, String rememberMe) {
        
            return new ResultDTO(200, "登录成功!");
        }
    

    当参数是个对象时,参数前加入
    @RequestBody
    这样才能将对象的属性给到swagger

     * 新增更新字典类型
         *
         * @author Qichang.Wang
         * @date 16:05 2018/6/15
         */
        @ApiOperation(value = "新增及更新字典",notes = "对象包含ID时为更新,不包含ID时为新增")
        @ApiImplicitParam(name = "dict",value = "字典对象,详细请查看对象属性",required = true,dataType = "DataDict")
        @RequestMapping(value = "/update", method = RequestMethod.POST)
        public ResultDTO update(@RequestBody @ModelAttribute DataDict dict) {
            return new ResultDTO(200,"操作成功");
        }
    

    4. web界面展示

    注解的各个属性相信各位大佬一看就能明了,不多加解释了
    加好注解后,重启应用
    接下来是见证奇迹的时候
    访问路径:应用地址+端口+swagger-ui.html 例如http://localhost:8800/swagger-ui.html

    image.png

    展开contoller效果


    image.png

    展开接口效果


    image.png

    点try it out 可以直接对接口进行测试,彻底抛弃什么Postman,RestfulClient等调试工具!

    只要注释写的全,再复杂的应用统统搞定有没有!!!
    改了接口自动重新生成API文档有没有!!!
    会写代码的前端工程师都能看懂有没有!!!
    还是看不懂的前端直接打屎算了有没有!!!

    美中不足是字段详细我还不确定能不能加注释,你们研究下吧,我觉得达到这效果已经OK了

    20180921补充:
    针对接口请求对象及返回对象使用上述方式无法展示字段说明。使用@ModelAttribute注解可能会导致一些莫名错误无法获取参数

    5. 实体注解

    @ApiModel 用于实体类上,标注为需要生成文档的实体
    @ApiModelProperty(value = "url菜单",required = true) 用于实体属性,标注该属性的说明,require 为false时不会在页面上进行展示该属性

    @Data
    @Accessors(chain = true)
    @TableName("t_resource")
    @ApiModel
    public class Resource extends Model<Resource> {
    
        private static final long serialVersionUID = 1L;
    
        /**
         * 资源ID
         */
        @TableId(type = IdType.UUID)
        @ApiModelProperty(hidden = true)
        private String id;
    
        /**
         * url
         */
        @ApiModelProperty(value = "url菜单",required = true)
        private String url;
    
        /**
         * 父ID
         */
        @TableField("parent_id")
        @ApiModelProperty(hidden = true)
        private String parentId;
    
        /**
         * 资源名
         */
        @TableField("resource_name")
        @ApiModelProperty(value = "资源名",required = true)
        private String resourceName;
    
        @Override
        protected Serializable pkVal() {
            return this.id;
        }
    
    }
    
    

    在实体添加注解后,在接口中使用如下注解

    @ApiParam(name = "资源对象",value = "json格式",required = true)
    标识该对象为文档实体

        @ApiOperation(value = "权限资源新增及更新", notes = "有ID更新,没ID为新增")
        @RequestMapping(value = "/resource", method = RequestMethod.POST)
        public ResultDTO resource(@RequestBody @ApiParam(name = "资源对象",value = "json格式",required = true) Resource resource) {
            Integer count = 0;
            try {
                count = userService.updateResources(resource);
            } catch (Exception e) {
                log.error("新增或更新资源异常:{}", e.getMessage());
                return new ResultDTO(500, e.getMessage());
            }
            return count > 0 ? new ResultDTO(200, "保存成功") : new ResultDTO(500, "操作失败,请查询日志");
        }
    

    实体注释文档界面

    需要点击model才会显示说明


    image.png

    效果如下


    image.png

    6. 关于shiro权限拦截

    使用中发现开启shiro后无法访问swagger api界面
    问题原因:swagger内置接口被权限拦截导致无法访问
    处理方式:
    浏览器打开F12调试,选择network,查看status为非200的以swagger、springfox、apidoc开头的URL,记录该URL并在权限拦截中放行

    image.png

    例如shiro,在ShiroFilter中放行以下URL。注意不同版本的swaggerURL略有区别,请以上面的方式查看具体需放行的URL

            filterChainDefinitionMap.put("/swagger-ui.html", "anon");
            filterChainDefinitionMap.put("/swagger-resources/**", "anon");
            filterChainDefinitionMap.put("/v2/api-docs", "anon");
            filterChainDefinitionMap.put("/webjars/springfox-swagger-ui/**", "anon");
    

    7. 前后分离之token Authorization 请求头设

    在前后分离项目中部分项目会使用到jwt或者其他方式在header中传递令牌token的方式,需要swagger添加相关配置加入请求头

    1. 在swaggerConfig中,加入全局header设置,参考如下代码
    /**
     * Created by wangqichang on 2018/9/25.
     */
    @Slf4j
    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
    
        @Value("${SwaggerSwitch:false}")
        private boolean SwaggerSwitch;
    
        @Bean
        public Docket createRestApi() {
            log.info("========================================== 当前环境是否开启Swagger:" + SwaggerSwitch);
            return new Docket(DocumentationType.SWAGGER_2)
                    .securitySchemes(securitySchemes())
                    .securityContexts(securityContexts())
                    .enable(SwaggerSwitch)
                    .apiInfo(apiInfo()).select()
                    .apis(RequestHandlerSelectors.basePackage("com.seebon.vip.csp.server.web.controller.rest")).paths(PathSelectors.any())
                    .build();
        }
    
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder().title("VIP后台系统API").description("新后台Restful接口").termsOfServiceUrl("http://www.xxx.com")
                    .version("1.0").build();
        }
    
        /**
         * swagger加入全局Authorization header 将在ui界面右上角新增token输入界面
         * @return
         */
        private List<ApiKey> securitySchemes() {
            ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header");
            ArrayList arrayList = new ArrayList();
            arrayList.add(apiKey);
            return arrayList;
        }
    
        /**
         * 在Swagger2的securityContexts中通过正则表达式,设置需要使用参数的接口(或者说,是去除掉不需要使用参数的接口),
         * 如下列代码所示,通过PathSelectors.regex("^(?!auth).*$"),
         * 所有包含"auth"的接口不需要使用securitySchemes。即不需要使用上文中设置的名为“Authorization”,
         * type为“header”的参数。
         *
         */
        private List<SecurityContext> securityContexts() {
            SecurityContext build = SecurityContext.builder()
                    .securityReferences(defaultAuth())
                    .forPaths(PathSelectors.any())
                    .build();
            ArrayList arrayList = new ArrayList();
            arrayList.add(build);
            return arrayList;
        }
    
        List<SecurityReference> defaultAuth() {
            AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
            AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
            authorizationScopes[0] = authorizationScope;
            SecurityReference authorization = new SecurityReference("Authorization", authorizationScopes);
            ArrayList arrayList = new ArrayList<>();
            arrayList.add(authorization);
            return arrayList;
        }
    }
    

    加入以上代码后,swagger-ui界面右上角将出现如图图标


    image.png

    点击该图标,在value中输入登录返回的token


    image.png
    然后请求任何接口,都会带上一个名为Authorization,值为输入值(例如本文中的token)的请求头了
    image.png

    8. 多项目多模块集成swagger

    在spring-cloud微服务项目下,每个服务都集成swagger后,我们有了多个swagger-ui.html地址,这时候往往不同的服务接口需要访问不同的URL才能看到对应的swagger.html界面

    如何将多个服务的swagger接口集中在一个页面进行展示?使用spring cloud zuul网关, 并做相应的配置即可

    1. zull网关启动类,开启swagger
    @SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
    @EnableEurekaClient
    @EnableZuulProxy
    @EnableSwagger2
    public class WebApplication {
        public static void main(String[] args) {
            SpringApplication.run(WebApplication.class);
        }
    }
    
    1. 实现接口SwaggerResourcesProvider 并加入注解@Component @Primary确保该类能被扫描为该接口的主配置
    /**
     * @author wangqichang
     * @since 2019/7/4
     */
    
    
    @Primary
    public class MySwaggerResourceProvider implements SwaggerResourcesProvider {
    
    
    
        @Override
        public List<SwaggerResource> get() {
            List resources = new ArrayList<>();
            resources.add(swaggerResource("系统及用户API", "/usermanager/v2/api-docs", "1.0"));
            resources.add(swaggerResource("线路及台账API", "/linemanager/v2/api-docs", "1.0"));
            resources.add(swaggerResource("文件服务管理API", "/filemanager/v2/api-docs", "1.0"));
            return resources;
        }
    
    
        private SwaggerResource swaggerResource(String name, String location, String version) {
            SwaggerResource swaggerResource = new SwaggerResource();
            swaggerResource.setName(name);
            swaggerResource.setLocation(location);
            swaggerResource.setSwaggerVersion(version);
            return swaggerResource;
        }
    }
    
    1. 效果展示
      在ui界面Select a spec中,出现了我们配置的三个SwaggerResource,当我们进行选择时,将直接访问改配置的URL。
      注意,使用zuul将地址路由到实际微服务的地址的/v2/api-docs接口
    image.png

    踩坑记录

    采坑1
    项目使用了sitemesh时,需要在decorators.xml中把上述资源进行排除,否则会影响到swagger-ui资源的访问,导致swagger-ui界面无内容

    采坑2
    在自定义权限控制中,访问/swagger-resources/configuration/ui资源时404,原因为通过@EnableSwagger2时会自动扫描到一个ApiResourceController类,该类有个资源路径为/configuration/ui
    而在自定义权限控制中该路径被拦截返回错误导致404

    相关文章

      网友评论

        本文标题:API文档生成之Springfox-Swagger框架的简单使用

        本文链接:https://www.haomeiwen.com/subject/usulyftx.html