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

后端开发一定厌倦了和前端对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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容