Spring Boot集成Swagger 2实现API接口文档管理

1. 引言

随着微服务架构体系的发展和应用, 为了前后端能够更好的集成与对接,同时为了项目的方便交付,每个项目都需要提供相应的API文档。

传统的API文档编写存在以下几个痛点:

  • 对API文档进行更新的时候,需要通知前端开发人员,导致文档更新交流不及时;
  • API接口返回信息不明确
  • 缺乏在线接口测试,通常需要使用相应的API测试工具,比如postman、SoapUI等
  • 接口文档太多,不便于管理

为了解决传统API接口文档维护的问题,为了方便进行测试后台Restful接口并实现动态的更新,因而引入Swagger接口工具。

Swagger具有以下优点:

  • 功能丰富:支持多种注解,自动生成接口文档界面,支持在界面测试API接口功能;
  • 及时更新:开发过程中花一点写注释的时间,就可以及时的更新API文档,省心省力;
  • 整合简单:通过添加pom依赖和简单配置,内嵌于应用中就可同时发布API接口文档界面,不需要部署独立服务。

当然也不能说Swagger就一定是完美的,它也有缺点,最明显的就是代码移入性比较强。

2. Swagger 2.0 集成配置

1. 添加Swagger2的Maven依赖

在Spring Boot项目中需要添加的依赖为Swagger 2核心包和swagger-ui界面包。

<dependencies>
       ... 项目其他依赖包 ...
       <!-- Swagger2 -->
       <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>
<dependencies>

2. 创建Swagger2配置文件

package com.gayond.hurricane.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.ArrayList;
import java.util.List;

@Configuration
public class SwaggerConfig {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.garyond.hurricane.rest")) // 配置需要扫描的包路径
                .paths(PathSelectors.any())
                .build();
                //.globalOperationParameters(pars);
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("身份认证服务")
                .description("提供统一的身份认证与身份验证服务")
                .termsOfServiceUrl("http://www.baidu.com")
                .version("0.0.1")
                .build();
    }

}

说明:

  • @Configuration 注解该类,等价于在Spring XML配置文件中beans配置;
  • @Bean 标注方法等价于Srping XML配置文件中的bean配置。

3. Spring Boot主应用中启用Swagger配置

需要在Spring Boot主应用中添加 @EnableSwagger2 注解说明启用Swagger服务

package com.garyond.hurricane.myservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@SpringBootApplication
@EnableSwagger2
public class JwtServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(JwtServiceApplication.class, args);
    }
}

3. Restful接口定义与展示

1. 在Spring Controller中使用Swagger注解

package com.garyond.hurricane.rest;

import com.garyond.hurricane.myservice.annotation.RequiredPerms;
import com.garyond.hurricane.myservice.controller.BaseController;
import com.garyond.hurricane.myservice.entity.JsonResult;
import com.garyond.hurricane.myservice.model.User;
import com.garyond.hurricane.myservice.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * 用户操作Restful接口
 *
 * @author Garyond
 * @version  1.0
 */
@RestController
@Api(value = "/v1/user", description = "用户CRUD操作接口")
@RequestMapping("/v1/user")
public class UserRestController extends BaseController{

    @Autowired
    private UserService userService;

    /**
     * 检索用户信息
     *
     * @return
     */
    @RequestMapping(value = "/query", method = {RequestMethod.GET})
    @RequiredPerms("user:list")
    @ApiOperation(value="根据用户名查询用户信息", notes="根据用户名查询用户信息")
    public @ResponseBody JsonResult queryUsers(@ApiParam(name="username", value = "用户名", required = true) @RequestParam("username") String username) {

        if (null == username || StringUtils.isBlank(username)) {
            return this.renderError("用户名不能为空");
        }

        User user = userService.getUserByUsername(username);

        if (null != user) {
            return this.renderSuccess(user);
        } else {
            return this.renderError("无用户数据");
        }
    }

    /**
     * 获取所有用户信息
     *
     * @return
     */
    @RequestMapping(method = {RequestMethod.GET})
    @RequiredPerms("user:list")
    @ApiOperation(value="获取用户列表", notes="获取用户列表")
    public @ResponseBody JsonResult listUsers() {

        List<User> userList = userService.findAll();

        if (null != userList && userList.size() > 0) {
            return this.renderSuccess(userList);
        } else {
            return this.renderError("无用户数据");
        }
    }

    /**
     * 获取用户信息
     *
     * @param userId
     * @return
     */
    @RequestMapping(value = "/{userId}",method = {RequestMethod.GET})
    @RequiredPerms("user:list")
    @ApiOperation(value="根据UserId获取用户信息", notes="根据UserId获取用户信息")
    @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "String", paramType = "path")
    public @ResponseBody JsonResult getUser(@PathVariable("userId") String userId) {
        User user = userService.getUserById(userId);

        if (null != user) {
            return this.renderSuccess(user);
        } else {
            return this.renderError();
        }
    }

    /**
     * 保存用户信息
     *
     * @param user
     * @return
     */
    @RequestMapping(method = {RequestMethod.POST})
    @RequiredPerms("user:add")
    @ApiOperation(value="添加用户信息", notes="添加用户信息")
    @ApiImplicitParam(name = "user", value = "用户信息", required = true, dataType = "User")
    public @ResponseBody JsonResult saveUser(User user) {
        userService.save(user);
        return this.renderSuccess();
    }

    /**
     * 更新用户信息
     *
     * @param user
     * @return
     */
    @RequestMapping(method = {RequestMethod.PUT})
    @RequiredPerms("user:update")
    @ApiOperation(value="更新用户信息", notes="更新用户信息")
    @ApiImplicitParam(name = "user", value = "用户信息", required = true, dataType = "User")
    public @ResponseBody JsonResult updateUser(User user) {
        userService.update(user);
        return this.renderSuccess();
    }

    /**
     * 删除用户信息
     *
     * @param userId
     * @return
     */
    @RequestMapping(value = "/{userId}", method = {RequestMethod.DELETE})
    @RequiredPerms("user:list")
    @ApiOperation(value="根据UserId删除用户信息", notes="根据UserId删除用户信息")
    @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "String", paramType = "path")
    public @ResponseBody JsonResult removeUser(@PathVariable("userId") String userId) {
        userService.removeByUserId(userId);
        return this.renderSuccess();
    }

}

2. 查看Swagger 2文档

启动SpringBoot项目,访问 http://localhost:8080/swagger-ui.html

Swagger文档展示
接口详情

4. Swagger 2常用注解说明

Swagger 2通过注解表明该API接口会生成文档,包括接口名称、请求方法、请求参数、返回信息的等等。

Swagger 2.0使用的注解及其说明:

  • @Api:用在类上,说明该类的作用。
  • @ApiOperation:注解来给API增加方法说明。
  • @ApiImplicitParams : 用在方法上包含一组参数说明。
  • @ApiImplicitParam:用来注解来给方法入参增加说明。
  • @ApiResponses:用于表示一组响应
  • @ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息
  • @ApiModel:描述一个Model的信息(一般用在请求参数无法使用@ApiImplicitParam注解进行描述的时候)
  • @ApiIgnore:使用该注解忽略这个API
  • @ApiError :发生错误返回的信息
作用范围 API 使用位置
对象属性 @ApiModelProperty 用在出入参数对象的字段上
协议集描述 @Api 用于controller类上
协议描述 @ApiOperation 用在controller的方法上
Response集 @ApiResponses 用在controller的方法上
Response @ApiResponse 用在 @ApiResponses里边
非对象参数集 @ApiImplicitParams 用在controller的方法上
非对象参数描述 @ApiImplicitParam 用在@ApiImplicitParams的方法里边
描述返回对象的意义 @ApiModel 用在返回对象类上

关于Swagger注解的使用可参考Swagger常用注解说明
更多信息可以参考【Swagger官方手册

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

推荐阅读更多精彩内容