赞
踩
前言:如果你是后台开发,提供restful接口给前端,建议你使用Swagger3提供restful的接口文档自动生成和在线接口调试。knife4j是对Swagger进一步封装,其优化了API文档的UI界面,是本人最推荐的方式。
Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。Swagger拥有接口文档自动生成和在线调试接口的两大功能。Swagger拥有众多不同语言和平台的开源实现与工具,他有很多实现方式,非常方便,并且支持语言特别多。
(1) Swagger是一组开源项目,其中主要项目如下:
Swagger-tools:提供各种与Swagger进行集成和交互的工具。例如模式检验、Swagger 1.2文档转换成Swagger 2.0文档等功能。
Swagger-core: 用于Java/Scala的的Swagger实现。与JAX-RS(Jersey、Resteasy、CXF...)、Servlets和Play框架进行集成。
Swagger-js: 用于JavaScript的Swagger实现。
Swagger-node-express: Swagger模块,用于node.js的Express web应用框架。
Swagger-ui:一个无依赖的HTML、JS和CSS集合,可以为Swagger兼容API动态生成优雅文档。
Swagger-codegen:一个模板驱动引擎,通过分析用户Swagger资源声明以各种语言生成客户端代码。
(2) springfox-swagger
在Spring中集成Swagger会使用到springfox-swagger
,它对Spring和Swagger的使用进行了整合
springfox是Java对swagger的一个具体实现。springfox的前身是swagger-springmvc,用于springmvc与swagger的整合。它内部会自动解析Spring容器中Controller暴露出的接口,并且也提供了一个界面用于展示或调用这些API。
Springfox其实是一个通过扫描代码提取代码中的信息,生成API文档的工具
。在Swagger的教程中,都会提到@Api
、@ApiModel
、@ApiOperation
这些注解,这些注解其实不是Springfox的,而是Swagger
的。springfox-swagger2
这个包依赖了swagger-core
这个包,而这些注解正是在这里面。但是,swagger-core
这个包是只支持JAX-RS2
的,并不支持常用的Spring MVC
。这就是springfox-swagger
的作用了,它将上面那些用于JAX-RS2
的注解适配到了Spring MVC
上。
Spring项目引入依赖:
- <dependency>
- <groupId>io.springfox</groupId>
- <artifactId>springfox-swagger2</artifactId>
- <version>${springfox.swagger.version}</version>
- </dependency>
- <dependency>
- <groupId>io.springfox</groupId>
- <artifactId>springfox-swagger-ui</artifactId>
- <version>${springfox.swagger.version}</version>
- </dependency>
SpingBoot项目引入依赖:
- <dependency>
- <groupId>io.springfox</groupId>
- <artifactId>springfox-boot-starter</artifactId>
- <version>3.0.0</version>
- </dependency>
(3)Swagger 3 的使用
Swagger2(基于openApi2)已经在17年停止维护了,取而代之的是 Swagger3(基于openApi3),而国内 Swagger3使用的文档较少,百度搜出来的都是过时的Swagger2(17年停止维护并更名为swagger3)的使用。
Open API:OpenApi是业界真正的 api 文档标准,其是由 Swagger 来维护的,并被linux列为api标准,从而成为行业标准。
Swagger组织:swagger 是一个 api 文档维护组织,后来成为了 Open API 标准的主要定义者,现在最新的版本为17年发布的 Swagger3(Open Api3)。
swagger2的包名为 io.swagger,而swagger3的包名为 io.swagger.core.v3。
Swagger 3 相关特性:
支持 Spring 5,Webflux(仅请求映射支持,尚不支持功能端点)、Spring Integration
补充官方在 spring boot 的自动装配 springfox-boot-starter 以后可以直接依赖一个 dependency
与2.0更好的规范兼容性
支持OpenApi 3.0.3
轻依赖 spring-plugin,swagger-core
现有的swagger2批注将继续有效并丰富开放式API 3.0规范
springfox-swagger-ui
如果使用springfox-swagger-ui
,启动项目后的api文档访问路径:http://localhost:8080/swagger-ui.html
swagger-bootstrap-ui是springfox-swagger的增强UI实现,api文档结构更加清晰,在线调试也很方便
- <dependency>
- <groupId>com.github.xiaoymin</groupId>
- <artifactId>swagger-bootstrap-ui</artifactId>
- <version>${swagger.bootstrap.ui.version}</version>
- </dependency>
访问的url为:http:// http://localhost:8080/doc.html
Knife4j在更名之前,原来的名称是叫swagger-bootstrap-ui,这是两种不一样风格的ui显示,将原来的蓝色变成炫酷的黑色模式,比传统的
springfox-swagger-ui和
swagger-bootstrap-ui更好看
,Knife4j是本人最推荐的方式。
Knifej是使用knife4j-spring-boot-starter的风格来编写的,可以将配置项写在配置文件中,这些配置项提供了许多增强功能,可以更好的整合springboot、springcloud;
Knifej执行更新,为了更平滑的演进,而swagger-bootstrap-ui已停更;
SpringDoc也是 Spring 社区维护的一个项目(非官方),帮助使用者将 swagger3 集成到 Spring 中。也是用来在 Spring 中帮助开发者生成文档,并可以轻松的在SpringBoot中使用。SpringDoc基于swagger,并做了更多的对Spring系列各框架的兼容,用法上与Swagger3基本相同,并多了一些自己的配置,相较于Swagger3来说更好用,支持也更好一点。
从 spring-fox 迁移到 springdoc,需要依赖变更:pom.xml 里去掉 springfox 或者 swagger 的依赖,并添加springdoc-openapi-ui
。
- <dependency>
- <groupId>org.springdoc</groupId>
- <artifactId>springdoc-openapi-ui</artifactId>
- <version>1.3.1</version>
- </dependency>
SpringDoc使用 swagger3 注解代替 swagger2 的:
这一步是可选的,因为改动太大,故 springfox对旧版的 swagger做了兼容处理。但不知道未来会不会不兼容,这里列出如何用 swagger 3 的注解代替 swagger 2 的,注意修改 swagger 3 注解的包路径为io.swagger.v3.oas.annotations。
Swagger2 的注解命名以易用性切入,全是 Api 开头,在培养出使用者依赖注解的习惯后,Swagger 3将注解名称规范化和工程化:
如果感兴趣,可以参考:SpringBoot结合SpringDo
Swagger用来自动生成API接口文档,还可以在线调试;而knife4j是对Swagger进一步封装,其优化了API文档的UI界面。
Knife4j
的前身是swagger-bootstrap-ui,
取名knife4j是希望她能像一把匕首一样小巧轻量并且功能强悍,希望把她做成一个为Swagger接口文档服务的通用性解决方案,不仅仅只是专注于UI前端。
SpringFox是对Swagger的SpringMVC的支持融合,Knife4是对Swagger进一步封装,其优化了api文档的界面
Knife4j在更名之前,原来的名称是叫swagger-bootstrap-ui。Knife4j底层依赖springfox,因此无需再单独引入springfox的具体版本
- <dependency>
- <groupId>com.github.xiaoymin</groupId>
- <artifactId>knife4j-spring-boot-starter</artifactId>
- <version>3.0.3</version>
- </dependency>
- import com.hs.notice.entity.EmailNotice;
- import com.hs.notice.entity.SmsNotice;
- import com.hs.notice.entity.WeChatNotice;
- import com.fasterxml.classmate.TypeResolver;
- import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.core.env.Environment;
- import org.springframework.core.env.Profiles;
- import springfox.documentation.builders.ApiInfoBuilder;
- import springfox.documentation.builders.PathSelectors;
- import springfox.documentation.builders.RequestHandlerSelectors;
- import springfox.documentation.service.ApiInfo;
- import springfox.documentation.service.Contact;
- import springfox.documentation.spi.DocumentationType;
- import springfox.documentation.spring.web.plugins.Docket;
- import springfox.documentation.swagger2.annotations.EnableSwagger2;
-
- /**
- * 作用: 自动生成API文档和在线接口调试工具
- */
-
- @Configuration
- //该注解是Springfox-swagger框架提供的使用Swagger注解,该注解必须加
- @EnableSwagger2
- //knife4j提供的增强扫描注解,Ui提供了例如动态参数、参数过滤、接口排序等增强功能
- @EnableKnife4j
- public class Knife4jConfig {
-
- /**
- * 创建一个Docket的对象,相当于是swagger的一个实例 : 配置开发和测试环境下开启Swagger,生产发布时关闭
- *
- * RequestHandlerSelectors,配置要扫描接口的方式
- * basePackage:指定扫描的包路径
- * any:扫描全部
- * none:全部不扫描
- * withClassAnnotation:扫描类上的注解,如RestController
- * withMethodAnnotation:扫描方法上的注解,如GetMapping
- *
- * @return
- */
- @Autowired
- TypeResolver typeResolver;
- @Bean
- public Docket createRestApi(Environment environment)
- {
- //设置显示的swagger环境信息,判断是否处在自己设定的环境当中,为了安全生产环境不开放Swagger
- Profiles profiles=Profiles.of("dev","test");
- boolean flag=environment.acceptsProfiles(profiles);
- //创建一个Docket的对象,相当于是swagger的一个实例
- return new Docket(DocumentationType.SWAGGER_2)
- .useDefaultResponseMessages(false)
- .groupName("1.x版本")
- .apiInfo(apiInfo())
- //只有当springboot配置文件为dev或test环境时,才开启swaggerAPI文档功能
- .enable(flag)
- .select()
- // 这里指定Controller扫描包路径:设置要扫描的接口类,一般是Controller类
- .apis(RequestHandlerSelectors.basePackage("com.hs.notice.controller")) //这里采用包扫描的方式来确定要显示的接口
- // .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) //这里采用包含注解的方式来确定要显示的接口
- // 配置过滤哪些,设置对应的路径才获取
- .paths(PathSelectors.any())
- .build()
- //防止Controller中参数中没有实体类或者返回值不是实体类导致Swagger Models页面扫描不到的情况
- .additionalModels(typeResolver.resolve(EmailNotice.class))
- .additionalModels(typeResolver.resolve(SmsNotice.class))
- .additionalModels(typeResolver.resolve(WeChatNotice.class));
- }
-
- ///配置相关的api信息
- private ApiInfo apiInfo()
- {
- return new ApiInfoBuilder()
- .description("API调试文档")
- //作者信息
- .contact(new Contact("何哥", "http://ip地址:8086/doc.html", "110@qq.com"))
- .version("v1.0")
- .title("消息通知服务API文档")
- //服务Url
- .termsOfServiceUrl("")
- .build();
- }
- }
如上代码所示,通过@Configuration注解,让Spring来加载该类配置。
DocumentationType.SWAGGER_2告诉Docketbean我们正在使用Swagger规范的版本2。
apiInfo()用来创建该Api的基本信息(这些基本信息会展现在文档页面中)。
select()创建一个构建器,用于定义哪些控制器及其生成的文档中应包含哪些方法。
apis()定义要包含的类(控制器和模型类)。这里我们包括所有这些,但您可以通过基础包,类注释等来限制它们。SpringFox会将其检测为文档生成源。Controller和Model类。您可以在Docket配置中轻松配置它。我们可以使用.apis(RequestHandlerSelectors.any(来包含所有类;当然我们也可以缩小到我们的基础包。
paths()允许您根据路径映射定义应包含哪个控制器的方法。我们现在包括所有这些,但您可以使用正则表达式等限制它。上面的代码.paths(PathSelectors.any())是代表匹配所有URL。
- import io.swagger.annotations.ApiModel;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
-
- @Data
- @ApiModel(value = "用户实体")
- public class User {
-
- @ApiModelProperty(value = "id")
- private Integer id;
-
- @ApiModelProperty(value = "用户名")
- private String username;
-
- @ApiModelProperty(value = "性别,0男,1女")
- private Integer sex;
- }
- package com.example.demo.controller;
-
- import com.example.demo.User;
- import io.swagger.annotations.*;
- import org.springframework.web.bind.annotation.*;
- import springfox.documentation.annotations.ApiIgnore;
-
- @RestController
- @Api(tags = "用户接口")//描述UserController的信息
- public class UserController {
-
- @ApiOperation(value = "查询用户",notes = "根据id查询用户")
- @ApiImplicitParam(paramType = "path",name="id",value = "用户id",required = true)
- @GetMapping("/user/query/{id}")
- public String getUserById(@PathVariable Integer id) {
- return "/user/"+id;
- }
-
- @ApiResponses({
- @ApiResponse(code=200,message="删除成功"),
- @ApiResponse(code=500,message="删除失败")})
- @ApiOperation(value = "删除用户",notes = "根据id删除用户")
- @DeleteMapping("/user/delete/{id}")
- public Integer deleteUserById(@PathVariable Integer id) {
- return id;
- }
-
- @ApiOperation(value = "添加用户",notes = "添加一个用户,传入用户名和性别")
- @ApiImplicitParams({
- @ApiImplicitParam(paramType = "query",name="username",value = "用户名",required = true,defaultValue = "张三"),
- @ApiImplicitParam(paramType = "query",name="sex",value = "性别",required = true,defaultValue = "女")
- })
- @PostMapping("/user")
- public String addUser(@RequestParam String username,@RequestParam String sex){
- return username+","+sex;
- }
-
- @ApiOperation(value="修改用户",notes = "根据传入的用户信息修改用户")
- @PutMapping("/user")
- public String updateUser(@RequestBody User user){
- return user.toString();
- }
-
- @GetMapping("/ignore")
- @ApiIgnore
- public void ignoreMethod(){}
-
- }
启动项目,在浏览器输入http://localhost:8080/doc.html就可以看到接口的信息,展开接口,就能看到所有的接口详细信息。
展开后可以对各个请求进行测试。选择接口后点击调试,输入相关的参数点击发送按钮即可。
页面简单清爽 ,可以导出接口文档非常方便,赶紧来试试吧
@Api:用在请求的类上,表示对类的说明
tags="说明该类的作用,可以在UI界面上看到的注解"
value="该参数没什么意义,在UI界面上也看到,所以不需要配置"
@ApiOperation:用在请求的方法上,说明方法的用途、作用
value="说明方法的用途、作用"
notes="方法的备注说明"
@ApiImplicitParams:用在请求的方法上,表示一组参数说明
@ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面
name:参数名
value:参数的汉字说明、解释
required:参数是否必须传
paramType:参数放在哪个地方
· header --> 请求参数的获取:@RequestHeader
· query --> 请求参数的获取:@RequestParam
· path(用于restful接口)--> 请求参数的获取:@PathVariable
· body(不常用)
· form(不常用)
dataType:参数类型,默认String,其它值dataType="Integer"
defaultValue:参数的默认值
@ApiResponses:用在请求的方法上,表示一组响应
@ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息
code:数字,例如400
message:信息,例如"请求参数没填好"
response:抛出异常的类
@ApiModel:用于响应类上,表示一个返回响应数据的信息
(这种一般用在post创建的时候,使用@RequestBody这样的场景,
请求参数无法使用@ApiImplicitParam注解进行描述的时候)
@ApiModelProperty:用在属性上,描述响应类的属性
- @Data
- @ApiModel(value="BizComponent对象", description="组件")
- public class BizComponent implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @TableId(value = "id", type = IdType.AUTO)
- private Long id;
-
- @ApiModelProperty(value = "分类")
- private Long categoryId;
-
- @ApiModelProperty(value = "组件名称")
- private String name;
-
- @ApiModelProperty(value = "组件描述")
- private String description;
-
- @ApiModelProperty(value = "日期字段")
- private LocalDateTime componentTime;
-
- @ApiModelProperty(value = "创建时间")
- @TableField(fill = FieldFill.INSERT)
- private LocalDateTime createTime;
-
- @ApiModelProperty(value = "修改时间")
- @TableField(fill = FieldFill.INSERT_UPDATE)
- private LocalDateTime modifiedTime;
- }
- @RestController
- @RequestMapping("/api/category")
- @Api(value = "/category", tags = "组件分类")
- public class BizCategoryController {
-
- private IBizCategoryService bizCategoryService;
-
- @GetMapping("/list")
- @ApiOperation(value = "列表", notes = "分页列表")
- public R<PageModel<BizCategory>> list(PageQuery pageQuery,
- @RequestParam @ApiParam("组件分类名称") String name) {
- IPage<BizCategory> page = bizCategoryService.page(pageQuery.loadPage(),
- new LambdaQueryWrapper<BizCategory>().like(BizCategory::getName, name));
- return R.success(page);
- }
-
- @GetMapping("/{categoryId}")
- @ApiOperation(value = "详情", notes = "组件分类详情")
- @ApiResponses({
- @ApiResponse(code=200,message="请求成功"),
- @ApiResponse(code=100,message="请求失败")})
- public R<BizCategory> detail(@PathVariable @ApiParam("分类Id") Long categoryId) {
- BizCategory category = bizCategoryService.getById(categoryId);
- return R.success(category);
- }
-
- @PostMapping("/save")
- @ApiOperation(value = "保存", notes = "新增或修改")
- @ApiImplicitParams({
- @ApiImplicitParam(paramType = "form", name = "categoryId", value = "组件id(修改时为必填)"),
- @ApiImplicitParam(paramType = "form", name = "name", value = "组件分类名称", required = true)
- })
- public R<BizCategory> save(Long categoryId, String name) {
- BizCategory category = new BizCategory();
- category.setId(categoryId);
- category.setName(name);
- bizCategoryService.saveOrUpdate(category);
- return R.success(category);
- }
-
- @DeleteMapping("/{categoryId}")
- @ApiOperation(value = "删除", notes = "删除")
- public R delete(@PathVariable @ApiParam("分类Id") Long categoryId) {
- bizCategoryService.delete(categoryId);
- return R.success();
- }
- }
某些Controller 不需要生成API文档的接口,可以通过@ApiIgnore忽略掉
虽然说swagger是个好东西,但是使用中切不可以忽略的一个安全问题。dev环境中你可以开放swagger给前端或者测试,但如果你的swagger ui不小心放到了生产,那是一件多么可怕的事情,真可以来个一锅端,切记切记。
- @Value("${swagger.switch}")
- private boolean swaggerSwitch;
-
- @Bean
- public Docket api() {
- Docket docket = new Docket(DocumentationType.SWAGGER_2);
- if (swaggerSwitch) {
- docket.enable(true);
- } else {
- docket.enable(false);
- }
-
- docket.apiInfo(apiInfo()).select().paths(PathSelectors.any()).build();
-
- return docket;
- }
方式一:只要在接口中,返回值中存在实体类,就会被扫描到swagger中
- @PostMapping("/account")
- public Account user(){
- return new Account();
- }
方式二:在controller 使用 @RequestBody 注解
Swagger的model里面之所以没有你需要的model,是因为你的model没有被swagger发现,我们利用Spring的注解让这个类被发现就行
- @ApiOperation(value = "添加用户", notes = "添加新用户")
- @PostMapping("/add")
- public Map addUser( @RequestBody Account account){
- return userService.createUser(account);
- }
方式三:在Swagger的配置Bean中,手动的添加你想呈现的类,如下最后三行,可以写很多个typeResolver.resolve(XXX.class)作为参数传入
- import com.fasterxml.classmate.TypeResolver;
-
- @Autowired
- TypeResolver typeResolver;
- @Bean
- public Docket createRestApi(Environment environment)
- {
- return new Docket(DocumentationType.SWAGGER_2)
- .useDefaultResponseMessages(false)
- .groupName("1.x版本")
- .apiInfo(apiInfo())
- .enable(true)
- .select()
- .apis(RequestHandlerSelectors.basePackage("com.hs.notice.controller"))
- .paths(PathSelectors.any())
- .build()
- //防止Controller中参数中没有实体类或者返回值不是实体类导致Swagger Models页面扫描不到的情况
- .additionalModels(typeResolver.resolve(EmailNotice.class))
- .additionalModels(typeResolver.resolve(SmsNotice.class))
- .additionalModels(typeResolver.resolve(WeChatNotice.class));
- }
报错信息如下:
- java.lang.NullPointerException: null
- at springfox.documentation.swagger2.mappers.RequestParameterMapper.bodyParameter(RequestParameterMapper.java:264) ~[springfox-swagger2-3.0.0.jar:3.0.0]
错误原因:
极有可能就是,给参数变量重命名了,导致Controller入参和API写的参数对应不上
比如下面代码中的Api提示参数的name = "errorDeviceState"
,但实际参数写的却是 errorDeviceStateEnum
- @ApiOperation("查看单个完整巡检报告")
- @GetMapping
- @ApiImplicitParams({
- @ApiImplicitParam(name = "inspectId", value = "巡检任务ID", dataTypeClass = String.class),
- @ApiImplicitParam(name = "errorDeviceState", value = "设备巡检状态(1异常,2离线)", dataTypeClass = ErrorDeviceStateEnum.class)
- })
- public WebResult<ReportVO> queryDetail(@RequestParam String inspectId, @RequestParam ErrorDeviceStateEnum errorDeviceStateEnum) {
- // code ...
- }
问题描述:请求参数本来应该是x-www-form-urlencoded方式的,却变成了raw方式,丢失content参数
解决方案:丢失参数content前面加上个@RequestParam注解就可以了
sendMessage(String openid,@RequestParam @Length(max=1024) String content,String appId,String t_sign,String requestId)
localhost/doc.html换为127.0.0.1/doc.html
参考链接:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。