前言

目前来说,在 Java 领域使用 Springboot 构建微服务是比较流行的,在构建微服务时,我们大多数会选择暴漏一个 REST API 以供调用。又或者公司采用前后端分离的开发模式,让前端和后端的工作由完全不同的工程师进行开发完成。不管是微服务还是这种前后端分离开发,维持一份完整的及时更新的 REST API 文档,会极大的提高我们的工作效率。而传统的文档更新方式(如手动编写),很难保证文档的及时性,经常会年久失修,失去应有的意义。因此选择一种新的 API 文档维护方式很有必要,这也是这篇文章要介绍的内容。

1. OpenAPI 规范介绍

OpenAPI Specification 简称 OAS,中文也称 OpenAPI 描述规范,使用 OpenAPI 文件可以描述整个 API,它制定了一套的适合通用的与语言无关的 REST API 描述规范,如 API 路径规范、请求方法规范、请求参数规范、返回格式规范等各种相关信息,使人类和计算机都可以不需要访问源代码就可以理解和使用服务的功能。

下面是 OpenAPI 规范中建议的 API 设计规范,基本路径设计规范。

https://api.example.com/v1/users?role=admin&status=active
\________________________/\____/ \______________________/
server URL endpoint query parameters
path

对于传参的设计也有规范,可以像下面这样:

OpenAPI 规范的东西远远不止这些,目前 OpenAPI 规范最新版本是 3.0.2,如果你想了解更多的 OpenAPI 规范,可以访问下面的链接。

OpenAPI Specification (https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md)

2. Swagger 介绍

很多人都以为 Swagger 只是一个接口文档生成框架,其实并不是。 Swagger 是一个围绕着 OpenAPI Specification(OAS,中文也称 OpenAPI规范)构建的一组开源工具。可以帮助你从 API 的设计到 API 文档的输出再到 API 的测试,直至最后的 API 部署等整个 API 的开发周期提供相应的解决方案,是一个庞大的项目。 Swagger 不仅免费,而且开源,不管你是企业用户还是个人玩家,都可以使用 Swagger 提供的方案构建令人惊艳的 REST API

Swagger 有几个主要的产品。

  • Swagger Editor – 一个基于浏览器的 Open API 规范编辑器。
  • Swagger UI – 一个将 OpenAPI 规范呈现为可交互在线文档的工具。
  • Swagger Codegen – 一个根据 OpenAPI 生成调用代码的工具。

如果你想了解更多信息,可以访问 Swagger 官方网站 https://swagger.io

3. Springfox 介绍

源于 Java 中 Spring 框架的流行,让一个叫做 Marrty Pitt 的老外有了为 SpringMVC 添加接口描述的想法,因此他创建了一个遵守 OpenAPI 规范(OAS)的项目,取名为 swagger-springmvc,这个项目可以让 Spring 项目自动生成 JSON 格式的 OpenAPI 文档。这个框架也仿照了 Spring 项目的开发习惯,使用注解来进行信息配置。

后来这个项目发展成为 Springfox,再后来扩展出 springfox-swagger2 ,为了让 JSON 格式的 API 文档更好的呈现,又出现了 springfox-swagger-ui 用来展示和测试生成的 OpenAPI 。这里的 springfox-swagger-ui 其实就是上面介绍的 Swagger-ui,只是它被通过 webjar 的方式打包到 jar 包内,并通过 maven 的方式引入进来。

上面提到了 Springfox-swagger2 也是通过注解进行信息配置的,那么是怎么使用的呢?下面列举常用的一些注解,这些注解在下面的 Springboot 整合 Swagger 中会用到。

注解 示例 描述
@ApiModel @ApiModel(value = "用户对象") 描述一个实体对象
@ApiModelProperty @ApiModelProperty(value = "用户ID", required = true, example = "1000") 描述属性信息,执行描述,是否必须,给出示例
@Api @Api(value = "用户操作 API(v1)", tags = "用户操作接口") 用在接口类上,为接口类添加描述
@ApiOperation @ApiOperation(value = "新增用户") 描述类的一个方法或者说一个接口
@ApiParam @ApiParam(value = "用户名", required = true) 描述单个参数

更多的 Springfox 介绍,可以访问 Springfox 官方网站。

Springfox Reference Documentation (http://springfox.github.io)

4. Springboot 整合 Swagger

就目前来说 ,Springboot 框架是非常流行的微服务框架,在微服务框架下,很多时候我们都是直接提供 REST API 的。REST API 如果没有文档的话,使用者就很头疼了。不过不用担心,上面说了有一位叫 Marrty Pitt 的老外已经创建了一个发展成为 Springfox 的项目,可以方便的提供 JSON 格式的 OpenAPI 规范和文档支持。且扩展出了 springfox-swagger-ui 用于页面的展示。

需要注意的是,这里使用的所谓的 Swagger 其实和真正的 Swagger 并不是一个东西,这里使用的是 Springfox 提供的 Swagger 实现。它们都是基于 OpenAPI 规范进行 API 构建。所以也都可以 Swagger-ui 进行 API 的页面呈现。

4.1. 创建项目

如何创建一个 Springboot 项目这里不提,你可以直接从 Springboot 官方 下载一个标准项目,也可以使用 idea 快速创建一个 Springboot 项目,也可以顺便拷贝一个 Springboot 项目过来测试,总之,方式多种多样,任你选择。

下面演示如何在 Springboot 项目中使用 swagger2。

4.2. 引入依赖

这里主要是引入了 springfox-swagger2,可以通过注解生成 JSON 格式的 OpenAPI 接口文档,然后由于 Springfox 需要依赖 jackson,所以引入之。springfox-swagger-ui 可以把生成的 OpenAPI 接口文档显示为页面。Lombok 的引入可以通过注解为实体类生成 get/set 方法。

<dependencies>
<!-- Spring Boot web 开发整合 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-json</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency> <!-- 引入swagger2的依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency> <!-- jackson相关依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.4</version>
</dependency> <!-- Lombok 工具 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

4.3. 配置 Springfox-swagger

Springfox-swagger 的配置通过一个 Docket 来包装,Docket 里的 apiInfo 方法可以传入关于接口总体的描述信息。而 apis 方法可以指定要扫描的包的具体路径。在类上添加 @Configuration 声明这是一个配置类,最后使用 @EnableSwagger2 开启 Springfox-swagger2。

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 springfox.documentation.swagger2.annotations.EnableSwagger2; /**
* <p>
* Springfox-swagger2 配置
*
* @Author niujinpeng
* @Date 2019/11/19 23:17
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig { @Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("net.codingme.boot.controller"))
.paths(PathSelectors.any())
.build();
} private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("未读代码 API")
.description("公众号:未读代码(weidudaima) springboot-swagger2 在线借口文档")
.termsOfServiceUrl("https://www.codingme.net")
.contact("达西呀")
.version("1.0")
.build();
}
}

4.4. 代码编写

文章不会把所有代码一一列出来,这没有太大意义,所以只贴出主要代码,完整代码会上传到 Github,并在文章底部附上 Github 链接。

参数实体类 User.java,使用 @ApiModel @ApiModelProperty 描述参数对象,使用 @NotNull 进行数据校验,使用 @Data 为参数实体类自动生成 get/set 方法。

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat; import javax.validation.constraints.NotNull;
import java.util.Date; /**
* <p>
* 用户实体类
*
* @Author niujinpeng
* @Date 2018/12/19 17:13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(value = "用户对象")
public class User { /**
* 用户ID
*
* @Id 主键
* @GeneratedValue 自增主键
*/
@NotNull(message = "用户 ID 不能为空")
@ApiModelProperty(value = "用户ID", required = true, example = "1000")
private Integer id; /**
* 用户名
*/
@NotNull(message = "用户名不能为空")
@ApiModelProperty(value = "用户名", required = true)
private String username;
/**
* 密码
*/
@NotNull(message = "密码不能为空")
@ApiModelProperty(value = "用户密码", required = true)
private String password;
/**
* 年龄
*/
@ApiModelProperty(value = "用户年龄", example = "18")
private Integer age;
/**
* 生日
*/
@DateTimeFormat(pattern = "yyyy-MM-dd hh:mm:ss")
@ApiModelProperty(value = "用户生日")
private Date birthday;
/**
* 技能
*/
@ApiModelProperty(value = "用户技能")
private String skills;
}

编写 Controller 层,使用 @Api 描述接口类,使用 @ApiOperation 描述接口,使用 @ApiParam 描述接口参数。代码中在查询用户信息的两个接口上都添加了 tags = "用户查询" 标记,这样这两个方法在生成 Swagger 接口文档时候会分到一个共同的标签组里。

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import net.codingme.boot.domain.Response;
import net.codingme.boot.domain.User;
import net.codingme.boot.enums.ResponseEnum;
import net.codingme.boot.utils.ResponseUtill;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*; import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List; /**
* <p>
* 用户操作
*
* @Author niujinpeng
* @Date 2019/11/19 23:17
*/ @Slf4j
@RestController(value = "/v1")
@Api(value = "用户操作 API(v1)", tags = "用户操作接口")
public class UserController { @ApiOperation(value = "新增用户")
@PostMapping(value = "/user")
public Response create(@Valid User user, BindingResult bindingResult) throws Exception {
if (bindingResult.hasErrors()) {
String message = bindingResult.getFieldError().getDefaultMessage();
log.info(message);
return ResponseUtill.error(ResponseEnum.ERROR.getCode(), message);
} else {
// 新增用户信息 do something
return ResponseUtill.success("用户[" + user.getUsername() + "]信息已新增");
}
} @ApiOperation(value = "删除用户")
@DeleteMapping(value = "/user/{username}")
public Response delete(@PathVariable("username")
@ApiParam(value = "用户名", required = true) String name) throws Exception {
// 删除用户信息 do something
return ResponseUtill.success("用户[" + name + "]信息已删除");
} @ApiOperation(value = "修改用户")
@PutMapping(value = "/user")
public Response update(@Valid User user, BindingResult bindingResult) throws Exception {
if (bindingResult.hasErrors()) {
String message = bindingResult.getFieldError().getDefaultMessage();
log.info(message);
return ResponseUtill.error(ResponseEnum.ERROR.getCode(), message);
} else {
String username = user.getUsername();
return ResponseUtill.success("用户[" + username + "]信息已修改");
}
} @ApiOperation(value = "获取单个用户信息", tags = "用户查询")
@GetMapping(value = "/user/{username}")
public Response get(@PathVariable("username")
@NotNull(message = "用户名称不能为空")
@ApiParam(value = "用户名", required = true) String username) throws Exception {
// 查询用户信息 do something
User user = new User();
user.setId(10000);
user.setUsername(username);
user.setAge(99);
user.setSkills("cnp");
return ResponseUtill.success(user);
} @ApiOperation(value = "获取用户列表", tags = "用户查询")
@GetMapping(value = "/user")
public Response selectAll() throws Exception {
// 查询用户信息列表 do something
User user = new User();
user.setId(10000);
user.setUsername("未读代码");
user.setAge(99);
user.setSkills("cnp");
List<User> userList = new ArrayList<>();
userList.add(user);
return ResponseUtill.success(userList);
}
}

最后,为了让代码变得更加符合规范和好用,使用一个统一的类进行接口响应。

@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "响应信息")
public class Response {
/**
* 响应码
*/
@ApiModelProperty(value = "响应码")
private String code;
/**
* 响应信息
*/
@ApiModelProperty(value = "响应信息")
private String message; /**
* 响应数据
*/
@ApiModelProperty(value = "响应数据")
private Collection content;
}

4.5. 运行访问

直接启动 Springboog 项目,可以看到控制台输出扫描到的各个接口的访问路径,其中就有 /2/api-docs

这个也就是生成的 OpenAPI 规范的描述 JSON 访问路径,访问可以看到。

因为上面我们在引入依赖时,也引入了 springfox-swagger-ui 包,所以还可以访问 API 的页面文档。访问路径是 /swagger-ui.html,访问看到的效果可以看下图。

也可以看到用户查询的两个方法会归到了一起,原因就是这两个方法的注解上使用相同的 tag 属性。

4.7. 调用测试

springfox-swagger-ui 不仅是生成了 API 文档,还提供了调用测试功能。下面是在页面上测试获取单个用户信息的过程。

  1. 点击接口 [/user/{username}] 获取单个用户信息。
  2. 点击 **Try it out** 进入测试传参页面。
  3. 输入参数,点击 Execute 蓝色按钮执行调用。
  4. 查看返回信息。

下面是测试时的响应截图。

5. 常见报错

如果你在程序运行中经常发现像下面这样的报错。

java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) ~[na:1.8.0_111]
at java.lang.Long.parseLong(Long.java:601) ~[na:1.8.0_111]
at java.lang.Long.valueOf(Long.java:803) ~[na:1.8.0_111]
at io.swagger.models.parameters.AbstractSerializableParameter.getExample(AbstractSerializableParameter.java:412) ~[swagger-models-1.5.20.jar:1.5.20]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_111]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_111]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_111]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_111]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:536) [jackson-databind-2.5.4.jar:2.5.4]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:666) [jackson-databind-2.5.4.jar:2.5.4]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:156) [jackson-databind-2.5.4.jar:2.5.4]
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:113) [jackson-databind-2.5.4.jar:2.5.4]

那么你需要检查使用了 @ApiModelProperty 注解且字段类型为数字类型的属性上,@ApiModelProperty 注解是否设置了 example 值,如果没有,那就需要设置一下,像下面这样。

@NotNull(message = "用户 ID 不能为空")
@ApiModelProperty(value = "用户ID", required = true, example = "1000")
private Integer id;

文中代码都已经上传到 https://github.com/niumoo/springboot

参考文档

<完>

个人网站:https://www.codingme.net

如果你喜欢这篇文章,可以关注公众号,一起成长。

关注公众号回复资源可以没有套路的获取全网最火的的 Java 核心知识整理&面试核心资料。

Springboot 系列(十六)你真的了解 Swagger 文档吗?的更多相关文章

  1. SpringBoot+SpringCloud+vue+Element开发项目——集成Swagger文档

    在pom.xml文件中添加Maven依赖 <!--swagger--> <dependency> <groupId>io.springfox</groupId ...

  2. jQuery系列(六):jQuery的文档操作

    1.插入操作 (1) 语法: 父元素.append(子元素) 解释:追加某元素,在父元素中添加新的子元素.子元素可以为:stirng | element(js对象) | jquery元素 let ol ...

  3. SpringBoot系列:六、集成Swagger文档

    本篇开始介绍Api文档Swagger的集成 一.引入maven依赖 <dependency> <groupId>io.springfox</groupId> < ...

  4. SpringBoot系列(十二)过滤器配置详解

    SpringBoot(十二)过滤器详解 往期精彩推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列(三)配置文件 ...

  5. S3C2416裸机开发系列十六_sd卡驱动实现

    S3C2416裸机开发系列十六 sd卡驱动实现 象棋小子    1048272975 SD卡(Secure Digital Memory Card)具有体积小.容量大.传输数据快.可插拔.安全性好等长 ...

  6. SpringBoot系列(六)集成thymeleaf详解版

    SpringBoot系列(六)集成thymeleaf详解版 1. thymeleaf简介  1. Thymeleaf是适用于Web和独立环境的现代服务器端Java模板引擎.  2. Thymeleaf ...

  7. springboot成神之——swagger文档自动生成工具

    本文讲解如何在spring-boot中使用swagger文档自动生成工具 目录结构 说明 依赖 SwaggerConfig 开启api界面 JSR 303注释信息 Swagger核心注释 User T ...

  8. Swagger文档转Word 文档

    GitHub 地址:https://github.com/JMCuixy/SwaggerToWord/tree/developer 原创作品,转载请注明出处:http://www.cnblogs.co ...

  9. Spring Boot:整合Swagger文档

    综合概述 spring-boot作为当前最为流行的Java web开发脚手架,越来越多的开发者选择用其来构建企业级的RESTFul API接口.这些接口不但会服务于传统的web端(b/s),也会服务于 ...

随机推荐

  1. python类方法@classmethod与@staticmethod

    目录 python类方法@classmethod与@staticmethod 一.@classmethod 介绍 语法 举例 二.@staticmethod 介绍 语法 举例 python类方法@cl ...

  2. webpack4+koa2+vue 实现服务器端渲染(详解)

    _ 阅读目录 一:什么是服务器端渲染?什么是客户端渲染?他们的优缺点? 二:了解 vue-server-renderer 的作用及基本语法. 三:与服务器集成 四:服务器渲染搭建 4.1 为每个请求创 ...

  3. Gstreamer基础教程10 - Streaming

    摘要 我们把直接从网络播放一个媒体文件的方式称为在线播放(Online Streaming),我们已经在以往的例子中体验了GStreamer的在线播放功能,当我们指定播放URI为 http:// 时, ...

  4. 在Eclipse中开发MapReduce程序

    一.Eclipse的安装与设置 1.在Eclipse官网上下载eclipse-jee-oxygen-3a-linux-gtk-x86_64.tar.gz文件并将其拷贝到/home/jun/Resour ...

  5. 写出float x 与“零值”比较的if语句——一道面试题分析

    写出float  x 与“零值”比较的if语句 请写出 float  x 与“零值”比较的 if 语句: const float EPSINON = 0.00001; if ((x >= - E ...

  6. ArangoDB数据导入

    目录 arangoimp方法 参数解析 实例展示 python方法 单条导入 批量导入 1.arangoimp方法 参数解析 全局配置部分(Global configuration) --backsl ...

  7. 全方面讲解TensorFlow

    任何曾经试图在 Python 中只利用 NumPy 编写神经网络代码的人都知道那是多么麻烦.编写一个简单的一层前馈网络的代码尚且需要 40 多行代码,当增加层数时,编写代码将会更加困难,执行时间也会更 ...

  8. 基于verdaccio的npm私有仓库搭建

    详见个人博客:https://shengchangwei.github.io/verdaccio/ 一.使用npm安装 npm install --global verdaccio 二.cmd 启动 ...

  9. 使用asp.net core 3.0 搭建智能小车2

    上一篇中我们把基本的运行环境搭建完成了,这一篇中,我们实战通过树莓派B+连接HC-SR04超声波测距传感器,用c# GPIO控制传感器完成距离测定,并将距离显示在网页上. 1.HC-SR04接线 传感 ...

  10. JavaSE(下)

    11.抽象的(abstract)方法是否同时是静态的(static),是否可同时是本地方法(native),是否可同时被synchronized? 都不能. 抽象方法需要子类重写,而静态的方法是无法被 ...