赞
踩
SpringBoot开发Restful接口,有什么API规范吗?如何快速生成API文档呢?Swagger 是一个用于生成、描述和调用 RESTful 接口的 Web 服务。通俗的来讲,Swagger 就是将项目中所有(想要暴露的)接口展现在页面上,并且可以进行接口调用和测试的服务。本文主要介绍OpenAPI规范,以及Swagger技术栈基于OpenAPI规范的集成方案。
OpenAPI规范(OAS)定义了一个标准的、语言无关的 RESTful API 接口规范,它可以同时允许开发人员和操作系统查看并理解某个服务的功能,而无需访问源代码,文档或网络流量检查(既方便人类学习和阅读,也方便机器阅读)。正确定义 OAS 后,开发者可以使用最少的实现逻辑来理解远程服务并与之交互。
此外,文档生成工具可以使用 OpenAPI 规范来生成 API 文档,代码生成工具可以生成各种编程语言下的服务端和客户端代码,测试代码和其他用例。官方GitHub地址: OpenAPI-Specification在新窗口打开
Swagger 是一个用于生成、描述和调用 RESTful 接口的 Web 服务。通俗的来讲,Swagger 就是将项目中所有(想要暴露的)接口展现在页面上,并且可以进行接口调用和测试的服务。
从上述 Swagger 定义我们不难看出 Swagger 有以下 3 个重要的作用:
Swagger3完全遵循了 OpenAPI 规范。Swagger 官网地址:https://swagger.io/在新窗口打开。
Swagger 可以看作是一个遵循了 OpenAPI 规范的一项技术,而 springfox 则是这项技术的具体实现。 就好比 Spring 中的 IOC 和 DI 的关系 一样,前者是思想,而后者是实现。
本质是Swagger的增强解决方案,前身只是一个SwaggerUI(swagger-bootstrap-ui)
Knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案, 前身是swagger-bootstrap-ui,取名kni4j是希望她能像一把匕首一样小巧,轻量,并且功能强悍!
Knife4j的前身是swagger-bootstrap-ui,为了契合微服务的架构发展,由于原来swagger-bootstrap-ui采用的是后端Java代码+前端Ui混合打包的方式,在微服务架构下显的很臃肿,因此项目正式更名为knife4j
更名后主要专注的方面
根据上文介绍,我们引入springfox依赖包,最新的是3.x.x版本。和之前的版本比,只需要引入如下的starter包即可。
- <dependency>
- <groupId>io.springfox</groupId>
- <artifactId>springfox-boot-starter</artifactId>
- <version>3.0.0</version>
- </dependency>
我们在配置中还增加了一些全局的配置,比如全局参数等
- package com.zhuangxiaoyan.springboot.swagger.config;
-
- import io.swagger.annotations.ApiOperation;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.http.HttpMethod;
- import springfox.documentation.builders.*;
- import springfox.documentation.oas.annotations.EnableOpenApi;
- import springfox.documentation.schema.ScalarType;
- import springfox.documentation.service.*;
- import springfox.documentation.spi.DocumentationType;
- import springfox.documentation.spring.web.plugins.Docket;
- import tech.pdai.springboot.swagger.constant.ResponseStatus;
-
- import java.util.ArrayList;
- import java.util.List;
- import java.util.stream.Collectors;
-
- /**
- * swagger config for open api.
- *
- * @author xjl
- */
- @Configuration
- @EnableOpenApi
- public class SwaggerConfig {
-
- /**
- * @return swagger config
- */
- @Bean
- public Docket openApi() {
- return new Docket(DocumentationType.OAS_30)
- .groupName("Test group")
- .apiInfo(apiInfo())
- .select()
- .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
- .paths(PathSelectors.any())
- .build()
- .globalRequestParameters(getGlobalRequestParameters())
- .globalResponses(HttpMethod.GET, getGlobalResponse());
- }
-
- /**
- * @return global response code->description
- */
- private List<Response> getGlobalResponse() {
- return ResponseStatus.HTTP_STATUS_ALL.stream().map(
- a -> new ResponseBuilder().code(a.getResponseCode()).description(a.getDescription()).build())
- .collect(Collectors.toList());
- }
-
- /**
- * @return global request parameters
- */
- private List<RequestParameter> getGlobalRequestParameters() {
- List<RequestParameter> parameters = new ArrayList<>();
- parameters.add(new RequestParameterBuilder()
- .name("AppKey")
- .description("App Key")
- .required(false)
- .in(ParameterType.QUERY)
- .query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
- .required(false)
- .build());
- return parameters;
- }
-
- /**
- * @return api info
- */
- private ApiInfo apiInfo() {
- return new ApiInfoBuilder()
- .title("Swagger API")
- .description("test api")
- .contact(new Contact("pdai", "http://pdai.tech", "suzhou.daipeng@gmail.com"))
- .termsOfServiceUrl("http://xxxxxx.com/")
- .version("1.0")
- .build();
- }
- }
- package com.zhuangxiaoyan.springboot.swagger.controller;
-
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiImplicitParam;
- import io.swagger.annotations.ApiOperation;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.annotation.*;
- import tech.pdai.springboot.swagger.entity.param.UserParam;
- import tech.pdai.springboot.swagger.entity.vo.AddressVo;
- import tech.pdai.springboot.swagger.entity.vo.UserVo;
-
- import java.util.Collections;
- import java.util.List;
-
- /**
- * @author xjl
- */
- @Api
- @RestController
- @RequestMapping("/user")
- public class UserController {
-
- /**
- * http://localhost:8080/user/add .
- *
- * @param userParam user param
- * @return user
- */
- @ApiOperation("Add User")
- @PostMapping("add")
- @ApiImplicitParam(name = "userParam", type = "body", dataTypeClass = UserParam.class, required = true)
- public ResponseEntity<String> add(@RequestBody UserParam userParam) {
- return ResponseEntity.ok("success");
- }
-
- /**
- * http://localhost:8080/user/list .
- *
- * @return user list
- */
- @ApiOperation("Query User List")
- @GetMapping("list")
- public ResponseEntity<List<UserVo>> list() {
- List<UserVo> userVoList = Collections.singletonList(UserVo.builder().name("dai").age(18)
- .address(AddressVo.builder().city("SZ").zipcode("10001").build()).build());
- return ResponseEntity.ok(userVoList);
- }
- }
这里展示目前使用Java生成接口文档的最佳实现: SwaggerV3(OpenAPI)+ Knife4J。
- <!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-spring-boot-starter -->
- <dependency>
- <groupId>com.github.xiaoymin</groupId>
- <artifactId>knife4j-spring-boot-starter</artifactId>
- <version>3.0.3</version>
- </dependency>
server: port: 8080 knife4j: enable: true documents: - group: Test Group name: My Documents locations: classpath:wiki/* setting: # default lang language: en-US # footer enableFooter: false enableFooterCustom: true footerCustomContent: MIT | zhuangxiaoyan # header enableHomeCustom: true homeCustomLocation: classpath:wiki/README.md # models enableSwaggerModels: true swaggerModelName: My Models
- package com.zhuangxiaoyan.springboot.knife4j.config;
-
- import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
- import io.swagger.annotations.ApiOperation;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.http.HttpMethod;
- import springfox.documentation.builders.*;
- import springfox.documentation.oas.annotations.EnableOpenApi;
- import springfox.documentation.schema.ScalarType;
- import springfox.documentation.service.*;
- import springfox.documentation.spi.DocumentationType;
- import springfox.documentation.spring.web.plugins.Docket;
- import tech.pdai.springboot.knife4j.constant.ResponseStatus;
-
- import java.util.ArrayList;
- import java.util.List;
- import java.util.stream.Collectors;
-
- /**
- * swagger config for open api.
- *
- * @author xjl
- */
- @Configuration
- @EnableOpenApi
- public class OpenApiConfig {
-
- /**
- * open api extension by knife4j.
- */
- private final OpenApiExtensionResolver openApiExtensionResolver;
-
- @Autowired
- public OpenApiConfig(OpenApiExtensionResolver openApiExtensionResolver) {
- this.openApiExtensionResolver = openApiExtensionResolver;
- }
-
- /**
- * @return swagger config
- */
- @Bean
- public Docket openApi() {
- String groupName = "Test Group";
- return new Docket(DocumentationType.OAS_30)
- .groupName(groupName)
- .apiInfo(apiInfo())
- .select()
- .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
- .paths(PathSelectors.any())
- .build()
- .globalRequestParameters(getGlobalRequestParameters())
- .globalResponses(HttpMethod.GET, getGlobalResponse())
- .extensions(openApiExtensionResolver.buildExtensions(groupName))
- .extensions(openApiExtensionResolver.buildSettingExtensions());
- }
-
- /**
- * @return global response code->description
- */
- private List<Response> getGlobalResponse() {
- return ResponseStatus.HTTP_STATUS_ALL.stream().map(
- a -> new ResponseBuilder().code(a.getResponseCode()).description(a.getDescription()).build())
- .collect(Collectors.toList());
- }
-
- /**
- * @return global request parameters
- */
- private List<RequestParameter> getGlobalRequestParameters() {
- List<RequestParameter> parameters = new ArrayList<>();
- parameters.add(new RequestParameterBuilder()
- .name("AppKey")
- .description("App Key")
- .required(false)
- .in(ParameterType.QUERY)
- .query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
- .required(false)
- .build());
- return parameters;
- }
-
- /**
- * @return api info
- */
- private ApiInfo apiInfo() {
- return new ApiInfoBuilder()
- .title("My API")
- .description("test api")
- .contact(new Contact("pdai", "http://xjl.tech", "suzhou.daipeng@gmail.com"))
- .termsOfServiceUrl("http://xxxxxx.com/")
- .version("1.0")
- .build();
- }
- }
- package com.zhaungxiaoyan.springboot.knife4j.constant;
-
- import lombok.AllArgsConstructor;
- import lombok.Getter;
-
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.List;
-
- /**
- * @author xjl
- */
- @Getter
- @AllArgsConstructor
- public enum ResponseStatus {
-
- SUCCESS("200", "success"),
- FAIL("500", "failed"),
-
- HTTP_STATUS_200("200", "ok"),
- HTTP_STATUS_400("400", "request error"),
- HTTP_STATUS_401("401", "no authentication"),
- HTTP_STATUS_403("403", "no authorities"),
- HTTP_STATUS_500("500", "server error");
-
- public static final List<ResponseStatus> HTTP_STATUS_ALL = Collections.unmodifiableList(
- Arrays.asList(HTTP_STATUS_200, HTTP_STATUS_400, HTTP_STATUS_401, HTTP_STATUS_403, HTTP_STATUS_500
- ));
-
- /**
- * response code
- */
- private final String responseCode;
-
- /**
- * description.
- */
- private final String description;
-
- }
- package com.zhuangxiaoyan.springboot.knife4j.controller;
-
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiImplicitParam;
- import io.swagger.annotations.ApiImplicitParams;
- import io.swagger.annotations.ApiOperation;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.annotation.PostMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
- import tech.pdai.springboot.knife4j.entity.param.AddressParam;
-
- /**
- * Address controller test demo.
- *
- * @author xjl
- */
- @Api(value = "Address Interfaces", tags = "Address Interfaces")
- @RestController
- @RequestMapping("/address")
- public class AddressController {
- /**
- * http://localhost:8080/address/add .
- *
- * @param addressParam param
- * @return address
- */
- @ApiOperation("Add Address")
- @PostMapping("add")
- @ApiImplicitParams({
- @ApiImplicitParam(name = "city", type = "query", dataTypeClass = String.class, required = true),
- @ApiImplicitParam(name = "zipcode", type = "query", dataTypeClass = String.class, required = true)
- })
- public ResponseEntity<String> add(AddressParam addressParam) {
- return ResponseEntity.ok("success");
- }
-
- }
上文我们看到可以通过Swagger系列可以快速生成API文档, 但是这种API文档生成是需要在接口上添加注解等,这表明这是一种侵入式方式; 那么有没有非侵入式方式呢, 比如通过注释生成文档? 本文主要介绍非侵入式的方式及集成Smart-doc案例。我们构建知识体系时使用Smart-doc这类工具并不是目标,而是要了解非侵入方式能做到什么程度和技术思路, 最后平衡下来多数情况下多数人还是会选择Swagger+openapi技术栈的。
既然有了Swagger, 为何还会产生Smart-Doc这类工具呢? 本质上是Swagger侵入性和依赖性。
我们来看下目前主流的技术文档工具存在什么问题:
smart-doc是一款同时支持JAVA REST API和Apache Dubbo RPC接口文档生成的工具,smart-doc在业内率先提出基于JAVA泛型定义推导的理念, 完全基于接口源码来分析生成接口文档,不采用任何注解侵入到业务代码中。你只需要按照java-doc标准编写注释, smart-doc就能帮你生成一个简易明了的Markdown、HTML5、Postman Collection2.0+、OpenAPI 3.0+的文档。
添加maven的插件
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- <plugin>
- <groupId>com.github.shalousun</groupId>
- <artifactId>smart-doc-maven-plugin</artifactId>
- <version>2.4.8</version>
- <configuration>
- <!--指定生成文档的使用的配置文件,配置文件放在自己的项目中-->
- <configFile>./src/main/resources/smart-doc.json</configFile>
- <!--指定项目名称,推荐使用动态参数,例如${project.description}-->
- <!--如果smart-doc.json中和此处都未设置projectName,2.3.4开始,插件自动采用pom中的projectName作为设置-->
- <!--<projectName>${project.description}</projectName>-->
- <!--smart-doc实现自动分析依赖树加载第三方依赖的源码,如果一些框架依赖库加载不到导致报错,这时请使用excludes排除掉-->
- <excludes>
- <!--格式为:groupId:artifactId;参考如下-->
- <!--也可以支持正则式如:com.alibaba:.* -->
- <exclude>com.alibaba:fastjson</exclude>
- </excludes>
- <!--includes配置用于配置加载外部依赖源码,配置后插件会按照配置项加载外部源代码而不是自动加载所有,因此使用时需要注意-->
- <!--smart-doc能自动分析依赖树加载所有依赖源码,原则上会影响文档构建效率,因此你可以使用includes来让插件加载你配置的组件-->
- <includes>
- <!--格式为:groupId:artifactId;参考如下-->
- <!--也可以支持正则式如:com.alibaba:.* -->
- <include>com.alibaba:fastjson</include>
- </includes>
- </configuration>
- <executions>
- <execution>
- <!--如果不需要在执行编译时启动smart-doc,则将phase注释掉-->
- <phase>compile</phase>
- <goals>
- <!--smart-doc提供了html、openapi、markdown等goal,可按需配置-->
- <goal>html</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
其中./src/main/resources/smart-doc.json是配置文件。
{ "serverUrl": "http://127.0.0.1", //服务器地址,非必须。导出postman建议设置成http://{{server}}方便直接在postman直接设置环境变量 "pathPrefix": "", //设置path前缀,非必须。如配置Servlet ContextPath 。@since 2.2.3 "isStrict": false, //是否开启严格模式 "allInOne": true, //是否将文档合并到一个文件中,一般推荐为true "outPath": "D://md2", //指定文档的输出路径 "coverOld": true, //是否覆盖旧的文件,主要用于markdown文件覆盖 "createDebugPage": true,//@since 2.0.0 smart-doc支持创建可以测试的html页面,仅在AllInOne模式中起作用。 "packageFilters": "",//controller包过滤,多个包用英文逗号隔开,2.2.2开始需要采用正则:com.test.controller.* "md5EncryptedHtmlName": false,//只有每个controller生成一个html文件时才使用 "style":"xt256", //基于highlight.js的代码高设置,可选值很多可查看码云wiki,喜欢配色统一简洁的同学可以不设置 "projectName": "pdai-springboot-demo-smart-doc",//配置自己的项目名称,不设置则插件自动获取pom中的projectName "skipTransientField": true,//目前未实现 "sortByTitle":false,//接口标题排序,默认为false,@since 1.8.7版本开始 "showAuthor":true,//是否显示接口作者名称,默认是true,不想显示可关闭 "requestFieldToUnderline":true,//自动将驼峰入参字段在文档中转为下划线格式,//@since 1.8.7版本开始 "responseFieldToUnderline":true,//自动将驼峰入参字段在文档中转为下划线格式,//@since 1.8.7版本开始 "inlineEnum":true,//设置为true会将枚举详情展示到参数表中,默认关闭,//@since 1.8.8版本开始 "recursionLimit":7,//设置允许递归执行的次数用于避免一些对象解析卡主,默认是7,正常为3次以内,//@since 1.8.8版本开始 "allInOneDocFileName":"index.html",//自定义设置输出文档名称, @since 1.9.0 "requestExample":"true",//是否将请求示例展示在文档中,默认true,@since 1.9.0 "responseExample":"true",//是否将响应示例展示在文档中,默认为true,@since 1.9.0 "ignoreRequestParams":[ //忽略请求参数对象,把不想生成文档的参数对象屏蔽掉,@since 1.9.2 "org.springframework.ui.ModelMap" ], "dataDictionaries": [{ //配置数据字典,没有需求可以不设置 "title": "http状态码字典", //数据字典的名称 "enumClassName": "tech.pdai.springboot.smartdoc.constant.ResponseStatus", //数据字典枚举类名称 "codeField": "responseCode",//数据字典字典码对应的字段名称 "descField": "description"//数据字典对象的描述信息字典 }], "errorCodeDictionaries": [{ //错误码列表,没有需求可以不设置 "title": "title", "enumClassName": "tech.pdai.springboot.smartdoc.constant.ResponseStatus", //错误码枚举类 "codeField": "responseCode",//错误码的code码字段名称 "descField": "description"//错误码的描述信息对应的字段名 }], "revisionLogs": [{ //文档变更记录,非必须 "version": "1.1", //文档版本号 "revisionTime": "2022-07-01 22:12:01", //文档修订时间 "status": "update", //变更操作状态,一般为:创建、更新等 "author": "pdai", //文档变更作者 "remarks": "init user api" //变更描述 },{ //文档变更记录,非必须 "version": "1.2", //文档版本号 "revisionTime": "2022-07-01 22:12:02", //文档修订时间 "status": "update", //变更操作状态,一般为:创建、更新等 "author": "pdai", //文档变更作者 "remarks": "add address api" //变更描述 } ], "customResponseFields": [{ //自定义添加字段和注释,一般用户处理第三方jar包库,非必须 "name": "code",//覆盖响应码字段 "desc": "响应代码",//覆盖响应码的字段注释 "ownerClassName": "org.springframework.data.domain.Pageable", //指定你要添加注释的类名 "ignore":true, //设置true会被自动忽略掉不会出现在文档中 "value": "00000"//设置响应码的值 }], "requestHeaders": [{ //设置请求头,没有需求可以不设置 "name": "token",//请求头名称 "type": "string",//请求头类型 "desc": "desc",//请求头描述信息 "value":"token请求头的值",//不设置默认null "required": false,//是否必须 "since": "-",//什么版本添加的改请求头 "pathPatterns": "/app/test/**",//请看https://gitee.com/smart-doc-team/smart-doc/wikis/请求头高级配置?sort_id=4178978 "excludePathPatterns":"/app/page/**"//请看https://gitee.com/smart-doc-team/smart-doc/wikis/请求头高级配置?sort_id=4178978 },{ "name": "appkey",//请求头 "type": "string",//请求头类型 "desc": "desc",//请求头描述信息 "value":"appkey请求头的值",//不设置默认null "required": false,//是否必须 "pathPatterns": "/test/add,/testConstants/1.0",//正则表达式过滤请求头,url匹配上才会添加该请求头,多个正则用分号隔开 "since": "-"//什么版本添加的改请求头 }], "requestParams": [ //设置公共参数,没有需求可以不设置 { "name": "configPathParam",//请求名称 "type": "string",//请求类型 "desc": "desc",//请求描述信息 "paramIn": "path", // 参数所在位置 header-请求头, path-路径参数, query-参数 "value":"testPath",//不设置默认null "required": false,//是否必须 "since": "2.2.3",//什么版本添加的该请求 "pathPatterns": "/app/test/**",//请看https://gitee.com/smart-doc-team/smart-doc/wikis/请求高级配置?sort_id=4178978 "excludePathPatterns":"/app/page/**"//请看https://gitee.com/smart-doc-team/smart-doc/wikis/请求高级配置?sort_id=4178978 }], "responseBodyAdvice":{ //自smart-doc 1.9.8起,非必须项,ResponseBodyAdvice统一返回设置(不要随便配置根据项目的技术来配置),可用ignoreResponseBodyAdvice tag来忽略 "className":"tech.pdai.springboot.smartdoc.entity.ResponseResult" //通用响应体 } }
可以通过Maven命令生成文档
- //生成html
- mvn -Dfile.encoding=UTF-8 smart-doc:html
在IDEA中,也可以通过maven插件构建
smart-doc 还支持生成如下类型的文档:
- //生成markdown
- mvn -Dfile.encoding=UTF-8 smart-doc:markdown
- //生成adoc
- mvn -Dfile.encoding=UTF-8 smart-doc:adoc
- //生成postman json数据
- mvn -Dfile.encoding=UTF-8 smart-doc:postman
- // 生成 Open Api 3.0+, Since smart-doc-maven-plugin 1.1.5
- mvn -Dfile.encoding=UTF-8 smart-doc:openapi
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。