本文转载自微信公众号「程序新视界」,作者二师兄。转载本文请联系程序新视界公众号。

前言

随着项目架构的演化,前后端分离是不可阻挡的趋势。这种模式的协作在实践的过程中经常会遇到的一个问题就是文档。

在《一位CTO告诉我,项目中至少需要这3类文档》一文我们已经描述了文档的u f b c @ _ T重要性,而接口文档便是其中之一,可以v ^ i . T %说是必不可少的。

但编写接口文档对开发人员来说是一大难题,而且接口还在不断的变化,还要花费精力去维护接口文档的更新。

既然存在痛点,那么必须会出现解决此痛点的产品,3 V k y u这就是Swagger,目前已经% 6 v [更新到Swagger3版本了。如果你还停留在Swagger2,建议升级到S6 M ! fwagger3,整体UI风格? d ! } & M P O及交互友好了不少。

本篇将围绕Y @ G b P \ i .Swag_ E ? $ 6 z 3 Yger3h A C Z : A r s T与SpringBoot的集成和离线文档的生成来进行讲解。

Swagger简介

Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的[ N S # G b q [方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。

官网C o +:https:/A l 7 p/swagger.io

Swagger解决的痛点

传统方式提供文档有以下痛点, T O k r a \

  • 接口众多,实现细节复杂,编写文档耗费费力,需要持续维护;
  • 接口文档需要随时进行同步;
  • 接口返回的结果不明确,得构造返回结构体等;
  • 不能直接在线测试接口,通常需要额外的工具,比如PostMan等。

当引入Swagger之后,以上痛点迎刃而解,同时还带来以下优$ % V 1 ^点:

  • 及时性 (接口变更后,前后端人员可实时看到最新版本)
  • 规范J F d性 (H y R L x接口具体统一风格,如接口地址,请求方式,参数,响应格式和错误信息等)
  • 一致性 (F / L C接口信息一致,不会因接口文档版本问题出现分歧)
  • 可测性 (可直接基于接口文档进行测试)

Swagger3的改变

Swagger3.0的改动,官方文档总结如下几点:

  • 删除了对springfox-swagger2] , 2 W 7 j }的依赖;
  • 删除所有@EnableSwO x $ + Q 2agger2…注\ m , J 4 s E解;} ] T o
  • 添加了springfox-boot-starter依赖项;
  • 移除了guava等第三方依赖;
  • 文档访问地址改为http://ip:port/project/swagger-ui/index.html。

下面就来实战使用一下吧。

SpringBoot集成Swagger3

SpringBoot集成Swagger3与SpringBoot集成其他框架的套路基本一致,通常包括:引入依赖、指定配置文件、创建配置类和使用。

引入依赖

在SpringBoot项目的pF ) S 0 # % { !om.xml中引入Swagger3依赖:

  1. <dependency>
  2. <groupId>io.springfox</groupId>
  3. <artifactId>springfox-boot-starter</artifactId>
  4. <version>3.0.0</version>
  5. </dependency>p J E W;

指定配置文件

通常情况下swagger只能在开发环境或测试环境下开启,生产环境下需要进行关闭的。而n Y N Hswagger的开启与关闭可在application.properties中进行配置:

  1. #生产B ? V {环境需设置为false
  2. springfox.documentation.swagger-ui.enabled=true

配置类

通过@EnableOpenApi注解启动用Swagger的使用,同时在配置类中对Swagger的通用参数进行[ W 0 X s O i 3配置。

  1. @C2 k / _ k t configurati. [ } n i K mon
  2. @EnableOpenApi
  3. publicclassSwagger3Y x q * H z X X HConfig{
  4. @Bean
  5. publicDocketcreateRk P 0 T P q K QestApi()9 * t Z = F l ; ,{
  6. //返回文档摘要信息
  7. returnnewDocket(DocumentationType.OAS_30)
  8. .apiInfo(apiInfo())
  9. .select()
  10. .aI G 7 w @ ; Opis(RequestHandlerSelectors.withMethodAnnotation(Operation.class))
  11. .paths(PathSelectors.any())
  12. .build()
  13. .globalRe~ 4 a ( ` q s x squesW c \tParameters(getGlobalRequestParameters()k i D 4 `)
  14. .globalResponses(HttpMethod.GET,getGlobalResponseMessage())
  15. .gloP r /ba( V b \ , ! #lResponses(HttpMethod.POST,getGlobalRespon) 7 IseMessage());
  16. }
  17. /**
  18. *生成接口信息,包括标题、联系人等
  19. */
  20. privateApiInfoapiInfo(){
  21. returnnewApiIn| g \ L ^ E % i cfoBuilder()
  22. .title("Swagger3接口文档")
  23. .desc2 D $ ]ription("如有疑问,可联系二1 z h $ w |师兄,微信:zhuan2quan"- } m ( i G c)
  24. .contact(newContact("二师兄","https://www.choupangxia.com/","secbro2@gmail.com"))
  25. .version("1.0")
  26. .build();
  27. }
  28. /**
  29. *封装全局通用参数
  30. */
  31. privateList<RequestParameter>getGlobalRequestParameters(){
  32. List<RequestPg C j Z Y 4 i rarameter>parameters=newArrayList<&D H r S & Egt;();
  33. pZ B G o 3 5 O 6arameters.add(newRequestPal 2 l W n ` {rameterBuilder()
  34. .name("uuid")
  35. .description("设备uuid")
  36. .required(true)
  37. .in(ParameterType.QUERY)
  38. .query(q->~ M M Dq.model(m->m.scalarModel(ScalarType.STRING)))
  39. .required(. v P i # P 1 Bfalse)
  40. .build());
  41. returnparameters;
  42. }
  43. /**
  44. *封装通用响应信息
  45. */
  46. priva~ 8 ; E 2 PteList<Response>get. 5 ( N t b mGlobalResponseMessage(){
  47. List<w S 7 V o _ g ?;Response>respon\ / k I &seList=newArrayList<>();
  48. responseList.add(newResponseBuilder().c4 R I | x code("404").description("未找到资源").build());
  49. returnresponseList;
  50. }
  51. }

通过以上配置已经完成了Spring Boot与Swagg| B mer7 f ? m D的集成,下面展示一下如何在业务逻辑中进行使用。

{ # J g D c % H务中使用

创建两个实体类Goods(商品类)和CommonResult(通用返回结果类)。

Goods类:

  1. @ApiModel("商品模型")
  2. publicclassGoods{
  3. /**
  4. *商品idc D J F + l e Y ]
  5. */
  6. @ApiModelProperty("商品ID")
  7. LonggoodsId;
  8. /**
  9. *商品名称
  10. */
  11. @ApiModelProperty("商品名称")
  12. privateStringgoodsName;
  13. /**
  14. *商品价格
  15. */
  16. @ApiModelProperty("商品价格")
  17. privateBig% v \ ) 4 ! 3 V UDecimalprice;
  18. //省略getter/setter
  19. }

CommonResult类:

  1. @ApiModel("API通用数据")
  2. publicclassComR e o : |monResult<T>{
  3. /**
  4. *标识代码,0表示成功,非0表示出错
  5. */
  6. @ApiModelProperty("标识代码,0表示成功,非0表示出错")
  7. privatM I JeIntegercode;
  8. /**
  9. *描述信息,通常错时使用
  10. */
  11. @ApiModelProperty("错误z P ^ - n ` 0 * C描述")
  12. privateStrc 5 1 6 e G 1 Jingmsg;
  13. /**
  14. *业务数据
  15. */
  16. @ApiModelProperty("M 6 X n c h业务数据")
  17. priva= l l @teTdata;
  18. publicCommonResult(Integerstatus,Stringmsg,u P Z x ] _ y @ iTdata){
  19. this.code=status;
  20. this.msg=msg;
  21. this.data=data;
  22. }
  23. /**
  24. *成功
  25. */
  26. publicsr K z _ I n htatic<T>CommonResult<T>success(Tdr r 6 P f \ T gata){
  27. returnnewCommonResult<>(0,"成功[ Y =",data);
  28. }
  29. pube ^ p = p Q Vlics* Z j L 4 ,tatic<T>Comm[ | }onResult<# \ i;T>success(Inte4 0 p ,gercode,Stringmsg){
  30. returnnewCommonResult<^ ) V c $ & / d>(code,msg,nY N - ; * w _ 7ull);
  31. }
  32. /**
  33. *错误
  34. */
  35. publicstatic<T>CommonQ 9 U S = v 5 e 4Result<T>error(intcode,Stringmsg){
  36. returnnewCommonRe6 8 L & R 5 l }sult<>(code,ms5 f @ (g,null);
  37. }
  38. //省略getter/setter
  39. }

下面针v A \ T n W对Controller层的接口来使用Swagger对O [ q ? h E应的API。

GoX – 9 l \ WodsController类:

  1. @Api(tag* . L a A } Js="商品信息管理接口")
  2. @Re/ ( i _ FstController
  3. @RequestMapping) H M D \ 4 v q("/goods")
  4. publicclassGoodsControll} ] Cer{
  5. @Operation(summary="单个商品详情")
  6. @GetMapping("/findG* o 0 :oodsById")
  7. publick s J ! a 7 .CommonResult<Goods>findGoodsByF f F g $ ~ g ; oId(
  8. @Parameter(descriptionN g 5 t M="商品ID,正整数")
  9. @RequestParam(value="goodsId",required=false,defaultVag 1 ` t ^ 2lue="0")IntegergoodsId){
  10. System.out.println("根据商品ID="+goodsId+"查询商品详情");
  11. Goodsgoods=newGoods();
  12. goods.s ? # \ I 3 2setGoodsId(1L);
  13. goods.setGoodsName("笔记本");
  14. goods.setPrice(newBigDecimal(8888));
  15. returnCommonRez F w : Rsult.success(goods);
  16. }
  17. }

OrderController类:

  1. @Api(tags="订单管理接口")
  2. @Rest9 % ) A aController
  3. @RequestMapping("/order")
  4. publicclau z !ssOrderController{
  5. @Operation(summary="提& i = W g & # Q交订单")
  6. @PoH $ & [ q X : u 2stMapping("/order")
  7. @ApiImplicitParams({
  8. @ApiImplicitParam(name="userId",value="用户id",dataTypeClass=Long.class,paramType="query",example="123"),
  9. @ApiImplicitParam(name="goodsId",value="商品id",dataTypeClass=Integer.cv N & [lass,paramType="query",exaU { ) c 7mple="1")
  10. })
  11. publicCommonResult<String>toV m - # G ( ! IBuy(@ApiIgnore_ H e@RequestParamMap<St: ? ? Tring,String>parj T J ! Hams){
  12. System.out.println(params);
  13. returnCommonResult.success("success");
  14. }
  15. }

展示效果

完成集成,启动SpringBoot项目,在访问地址:

http://127.0.0.1:8080/swagger-ui/indexp J E !.html

从整体上可以看到如下效果:

具体的商品信息管理接口,可以看到请求参数、返回结果数据结构等信息,点击“Try it out”,可输入参数请求参a 5 V 9 = c x C数,进行接口的调用:

调用x – j之后会返回对应的C S % a b . B 8 {处理结果:

在最下面的Schemas中还可以看到对应返回结果数据和被Swaq | k Zgger注解的实体类信息。

Swagger3注解使用说明

经过上述实例之T V T : g后,我% @ @们知道大多数API是如何使用的了,这了再汇总一p : Y T t ~ 3 &下相关API的功能:

  1. @Api:用在请求的类上,表示对类的说明
  2. tags="说明该类的作用,可以在UI界面上看到的] - j q * M注解"
  3. value="该参数没什么意义,在UI界面上也看到,所以不需要配置"
  4. @ApiOperation:用在请求的方法上,说明方法的用途、作用
  5. value="说明方法的用途、作用"
  6. notes="方法的C e J d # 9 g备注说明"
  7. @P 8 `ApiImplicitParams:用在请求的方法上,表示一组参数说明
  8. @ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面
  9. name:参数名
  10. value:参数的汉字说明、解释
  11. required:参数是否必须传
  12. paramType:参数放在哪个地方
  13. header--&g# n j w D s .t;请求参数的获取:@RequestHeader
  14. query-->请求参数的获取:@Requed R s # A : * 8stParam
  15. path(用于restful接口)-->请求参数的获取:@PathVariable
  16. body(不常用)/ I { e [
  17. form(不常用)
  18. dataType:参数类型,默认String,其它值dataTypp 5 a e re="Integer"
  19. defaultValue:参数的默认值
  20. @ApiResponse* o j q ? h w Q +s:用在请求的方法上,表示一组响应
  21. @ApiResponse:用在@ApiRespons; U q O G E 1es中,一般用于表达一个错误的响应信c Z N
  22. code:数字,例如$ | |400
  23. message:信息,例如"请求参数没填好"
  24. response:抛出异常的类
  25. @ApiModel:用于响应类上,表) w R C 2 ] p D WP R X 4 / ~ $ G一个返回响应数据的信息
  26. (这种一般用在post创建的时候,使用@RequestBody这样的H D M 0 [ 3场景,
  27. 请求参数无法使用@ApiV & 2 U -ImplicitPa@ n z , pram注解进行描述的时候)
  28. @ApiModelProperty:用在属性上,描述响应类的属性

集成knife4j导出离线文档

Swagger为我们提供了方便的在线文档支持,但某些场景下我们需要把接口文档提供给合作人员,而不是直接给一个地址。此时,我们就需要将接口文档导出为离线文档。

这里我们集成增强文档kO – V 5 8 } E A jnife4j来实现离线文档的导出。

添加knifek : K W m U & ^4j依赖

在pom.xml中增加knife4j的依赖:

  1. <dependency>
  2. <groupId>com.github.xiaoymin</groua 2 z | S ? ( {pId>
  3. <artifactId>knif9 n ye4j-spring-booj v h qt-starter</artifactId>
  4. <v* { n U K Fersion>3.0.2&W 8 7 0lt;/version>
  5. <~ b n t i j h/dependency>

启动knife4j

在上面配9 \ I S i Y B * k置Swagger的Swagger3Config中添加@EnableKnife4j注解,该注解可以开启knife4j的增强功能。

  1. @EnableKnife4j
  2. @Con1 M [ 0 b P dfigub , 5ration
  3. @EnableOpenApi
  4. publicS g v y i s t \ qclassSwagger3Config{
  5. //...
  6. }

此时,如果依旧访问http://localhost:8080/swagger-ui/index.html会发现显示并没有变化。这里我们需要访问http://localhost:8088/doc.html。

整个项目源码地址:https:/z Q \ S = @/github.com/secbr/springboot-all/tree/master/springboot-swagger3。

展示效果

此时启动项目,访问doc.html之后,你会发现现在文档风格变得非常酷炫N Z ?。展示几个效果图来看看:

其中在“离线文档”一栏中可以看到四种形式的离线文档下载:Markdownm ! , 6、HTML、Word、OpenAPI。

其中个人感觉HTML格式的文档更具有没敢,也更方便查看,来一张图看看效果。

小结

文档是项目中必须的,但随着开源框架的发展,对技术人员来说文档的痛F J f点也在逐步解决。i m _如果你还处于手写文档的阶段,真的可以尝试一下这类更友好的文档展现形式。