yeskery

Spring Boot 中使用 Swagger2 构建强大的 RESTful API 文档

由于 Spring Boot 能够快速开发、便捷部署等特性,相信有很大一部分 Spring Boot 的用户会用来构建 RESTful API。而我们构建 RESTful API 的目的通常都是由于多终端的原因,这些终端会共用很多底层业务逻辑,因此我们会抽象出这样一层来同时服务于多个移动端或者 Web 前端。

这样一来,我们的 RESTful API 就有可能要面对多个开发人员或多个开发团队:IOS 开发、Android 开发或是 Web 开发等。为了减少与其他团队平时开发期间的频繁沟通成本,传统做法我们会创建一份 RESTful API 文档来记录所有接口细节,然而这样的做法有以下几个问题:

  • 由于接口众多,并且细节复杂(需要考虑不同的HTTP请求类型、HTTP头部信息、HTTP请求内容等),高质量地创建这份文档本身就是件非常吃力的事,下游的抱怨声不绝于耳。

  • 随着时间推移,不断修改接口实现的时候都必须同步修改接口文档,而文档与代码又处于两个不同的媒介,除非有严格的管理机制,不然很容易导致不一致现象。

为了解决上面这样的问题,本文将介绍 RESTful API 的重磅好伙伴 Swagger2,它可以轻松的整合到 Spring Boot 中,并与 Spring MVC 程序配合组织出强大 RESTful API 文档。它既可以减少我们创建文档的工作量,同时说明内容又整合入实现代码中,让维护文档和修改代码整合为一体,可以让我们在修改代码逻辑的同时方便的修改文档说明。另外 Swagger2 也提供了强大的页面测试功能来调试每个 RESTful API。具体效果如下图所示:

swagger2_1

添加 Swagger2 依赖

pom.xml 中加入 Swagger2 的依赖

  1. <dependency>
  2. <groupId>io.springfox</groupId>
  3. <artifactId>springfox-swagger2</artifactId>
  4. <version>2.2.2</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>io.springfox</groupId>
  8. <artifactId>springfox-swagger-ui</artifactId>
  9. <version>2.2.2</version>
  10. </dependency>

创建Swagger2配置类

Application.java 同级创建 Swagger2 的配置类 Swagger2

  1. @Configuration
  2. @EnableSwagger2
  3. public class Swagger2 {
  4. @Bean
  5. public Docket createRestApi() {
  6. return new Docket(DocumentationType.SWAGGER_2)
  7. .apiInfo(apiInfo())
  8. .select()
  9. .apis(RequestHandlerSelectors.basePackage("com.didispace.web"))
  10. .paths(PathSelectors.any())
  11. .build();
  12. }
  13. private ApiInfo apiInfo() {
  14. return new ApiInfoBuilder()
  15. .title("Spring Boot中使用Swagger2构建RESTful APIs")
  16. .description("更多Spring Boot相关文章请关注:http://blog.didispace.com/")
  17. .termsOfServiceUrl("http://blog.didispace.com/")
  18. .contact("程序猿DD")
  19. .version("1.0")
  20. .build();
  21. }
  22. }

如上代码所示,通过 @Configuration 注解,让 Spring 来加载该类配置。再通过 @EnableSwagger2 注解来启用 Swagger2。

再通过 createRestApi 函数创建 Docket 的 Bean 之后,apiInfo() 用来创建该 Api 的基本信息(这些基本信息会展现在文档页面中)。select() 函数返回一个 ApiSelectorBuilder 实例用来控制哪些接口暴露给 Swagger 来展现,本例采用指定扫描的包路径来定义,Swagger 会扫描该包下所有 Controller 定义的 API,并产生文档内容(除了被 @ApiIgnore 指定的请求)。

添加文档内容

在完成了上述配置后,其实已经可以生产文档内容,但是这样的文档主要针对请求本身,而描述主要来源于函数等命名产生,对用户并不友好,我们通常需要自己增加一些说明来丰富文档内容。如下所示,我们通过 @ApiOperation 注解来给API增加说明、通过 @ApiImplicitParams@ApiImplicitParam 注解来给参数增加说明。

  1. @RestController
  2. @RequestMapping(value="/users") // 通过这里配置使下面的映射都在/users下,可去除
  3. public class UserController {
  4. static Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>());
  5. @ApiOperation(value="获取用户列表", notes="")
  6. @RequestMapping(value={""}, method=RequestMethod.GET)
  7. public List<User> getUserList() {
  8. List<User> r = new ArrayList<User>(users.values());
  9. return r;
  10. }
  11. @ApiOperation(value="创建用户", notes="根据User对象创建用户")
  12. @ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
  13. @RequestMapping(value="", method=RequestMethod.POST)
  14. public String postUser(@RequestBody User user) {
  15. users.put(user.getId(), user);
  16. return "success";
  17. }
  18. @ApiOperation(value="获取用户详细信息", notes="根据url的id来获取用户详细信息")
  19. @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long")
  20. @RequestMapping(value="/{id}", method=RequestMethod.GET)
  21. public User getUser(@PathVariable Long id) {
  22. return users.get(id);
  23. }
  24. @ApiOperation(value="更新用户详细信息", notes="根据url的id来指定更新对象,并根据传过来的user信息来更新用户详细信息")
  25. @ApiImplicitParams({
  26. @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long"),
  27. @ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
  28. })
  29. @RequestMapping(value="/{id}", method=RequestMethod.PUT)
  30. public String putUser(@PathVariable Long id, @RequestBody User user) {
  31. User u = users.get(id);
  32. u.setName(user.getName());
  33. u.setAge(user.getAge());
  34. users.put(id, u);
  35. return "success";
  36. }
  37. @ApiOperation(value="删除用户", notes="根据url的id来指定删除对象")
  38. @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long")
  39. @RequestMapping(value="/{id}", method=RequestMethod.DELETE)
  40. public String deleteUser(@PathVariable Long id) {
  41. users.remove(id);
  42. return "success";
  43. }
  44. }

完成上述代码添加上,启动 Spring Boot 程序,访问:http://localhost:8080/swagger-ui.html。就能看到前文所展示的 RESTful API 的页面。我们可以再点开具体的 API 请求,以 POST 类型的 /users 请求为例,可找到上述代码中我们配置的 Notes 信息以及参数 user 的描述信息,如下图所示。

swagger2_2

API文档访问与调试

在上图请求的页面中,我们看到 user 的 Value 是个输入框?是的,Swagger 除了查看接口功能外,还提供了调试测试功能,我们可以点击上图中右侧的 Model Schema(黄色区域:它指明了 User 的数据结构),此时 Value 中就有了 user 对象的模板,我们只需要稍适修改,点击下方 “Try it out!” 按钮,即可完成了一次请求调用!

此时,你也可以通过几个 GET 请求来验证之前的 POST 请求是否正确。

相比为这些接口编写文档的工作,我们增加的配置内容是非常少而且精简的,对于原有代码的侵入也在忍受范围之内。因此,在构建 RESTful API 的同时,加入 swagger 来对 API 文档进行管理,是个不错的选择。

在 Spring Boot 拦截器中排除 Swagger

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  4. import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
  5. import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
  6. @Configuration
  7. public class WebAppConfig extends WebMvcConfigurationSupport {
  8. @Bean
  9. LoginInterceptor localInterceptor() {
  10. return new LoginInterceptor();
  11. }
  12. @Override
  13. public void addInterceptors(InterceptorRegistry registry) {
  14. registry.addInterceptor(localInterceptor())
  15. .addPathPatterns("/**")
  16. .excludePathPatterns("/user/login")
  17. .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**");
  18. }
  19. @Override
  20. protected void addResourceHandlers(ResourceHandlerRegistry registry) {
  21. registry.addResourceHandler("swagger-ui.html")
  22. .addResourceLocations("classpath:/META-INF/resources/");
  23. registry.addResourceHandler("/webjars/**")
  24. .addResourceLocations("classpath:/META-INF/resources/webjars/");
  25. }
  26. }

本文部分内容来自:http://blog.didispace.com/springbootswagger2/

评论

发表评论 点击刷新验证码

提示

该功能暂未开放