您当前的位置: 首页 >  spring

springboot+Swagger2最佳实践和使用规范

发布时间:2020-03-13 16:37:11 ,浏览量:6

springboot+Swagger2最佳实践和使用规范 1. 前言

本文主要的内容是: 1、springboot整合Swagger2,如需要看这部分内容可以直接跳到对应章节 2、讨论swagger的使用规范,以及一些最佳实践。 认真看完,你会有收获的

swagger版本是:2.9.2(不同版本UI界面有可能不同)

swagger2和1,因为2的版本可能对比1升级比较大,所以叫2,其实还是swagger

2. 使用规范 2.1 准备

先准备基础的知识。

传参一般使用两种方式

  • 键值对

  • 传JSON

键值对准确说是 “名值对”,即Content-Type是x-www-form-urlencoded或者form-data的传参方式。

传JSON 的 Content-Type是application/json。注意区分 “传JSON字符串”,两者在请求body的内容不一样

// 传JSON字符串,body内容如下 jsonStr={"name":"Stone"} // 传JSON,如下,没有key的 {"name":"Stone"} 
2.2 Swagger使用规范

只用@Api、@ApiOperation、@ApiParam、@ApiModelProperty就够了

  • 禁止不指定请求方法,要指定用GET/POST…,用@RequestMapping不指定的时候会产生大量的垃圾接口 在这里插入图片描述

在如下地方使用swagger的注解要注意

  • 控制器

    必须注解@Api(tags = "..."),必须有tags

    @RestController @Api(tags = "用户模块") public class UserController {...} 

    不想暴露控制器里所有方法,用hiden属性,要隐藏单个接口,在@ApiOperation里使用hidden

  • 方法

    用 @ApiOperation,用value说明接口用途,notes用于更详细的说明。value必出现,notes可选,没有notes时value要省略以保证简洁

    // 要省略value,保证简洁,别 @ApiOperation(value = "获取用户信息") @ApiOperation("获取用户信息") @GetMapping("/user/get") public ResponseDTO<UserDO> getUser( // 当有更长的内容补充时,使用notes @ApiOperation(value = "获取用户列表", notes = "如果需要详细的补充描述,在这里写,在value里会让标题很长") @GetMapping("/user/list") public ResponseDTO<List<UserDO>> listUser( 
  • 方法入参

    • @ApiParam(value = “…”, required = true|false)

      任何控制器参数必须有这个注解(除HttpServletRequest…外)

      value和required任何情况都不能少(不论required是true是false)

    • @RequestParam(required = true|false)

      若使用该注解,required在任何情况都不可省略

      • 如果是单一类型,使用该注解(非单一类型例如类里有多个字段组成,单一类型就是仅一个类型)
      • 如果是类的类型,无论传键值对还是传JSON,都不能加上(否则swagger不能展类里的多字段)

    再次强调,required任何情况都不要省略,省略后不直观,参数再长也不过一行

    // 单一类型,要用 @RequestParam(required) @ApiOperation("获取用户信息") @GetMapping("/user/get") public ResponseDTO<UserDO> getUser( @ApiParam(value = "用户ID", required = true) @RequestParam(required = true) Integer id ) // 入参是一个类,禁止用 @RequestParam(required),这个例子是传键值对的 @ApiOperation("新增评论(键值对传参方式)") @PostMapping("/comment/operate") public ResponseDTO<Integer> operateComment( @ApiParam(value = "评论信息", required = true) @Valid CommentOperReqDTO commentOperReqDTO ) // 入参是个类,禁止用 @RequestParam(required),这个例子是传JSON的 @ApiOperation("新增订单") @PostMapping("/order/add") public ResponseDTO<OrderRespDTO> addOrder( @ApiParam(value = "订单信息", required = true) @Valid @RequestBody OrderReqDTO orderReqDTO ) 
  • 方法入参DTO

    • 只有类的类型才需要标注

    • DTO类上不需要任何注解,DTO所有字段都需要 @ApiModelProperty

      • 必须指定value和required,任何情况不得省略required,指定required是希望前端同学知道是否必填
      • 某个字段,比如ID,在新增时用不上可不填但是在修改时必填,这种情况让required=false,并且在value中指出什么情况下必填什么时候可选
      • 内嵌其他类的字段也必须使用注解
      • 不要用example,例如@ApiModelProperty(example = "ID", required = true)
      • 因为入参需要校验参数,有可能还会带jsr303的注解
    // 类上不用注解 // 没个字段都必须用 @ApiModelProperty // 内嵌的类 contactInfo 和集合 prodInfoList 也必须用 @ApiModelProperty // @ApiModelProperty 必须包含 value 和 required // 内嵌的类用静态内部类 @Data public class OrderReq2DTO { @NotNull @ApiModelProperty(value = "商品总价", required = true) private Long totalPriceInCent; @ApiModelProperty(value = "备注(如果总价大于1万,必须备注)", required = false) private String memo; @NotNull @Valid @ApiModelProperty(value = "联系信息", required = true) private ContactInfo contactInfo; @NotEmpty @Valid @ApiModelProperty(value = "商品信息列表", required = true) private List<ProdInfo> prodInfoList; @Data public static class ContactInfo { @NotBlank @ApiModelProperty(value = "邮寄地址", required = true) private String address; @NotBlank @ApiModelProperty(value = "手机", required = true) private String mobile; } @Data public static class ProdInfo { @ApiModelProperty(value = "商品ID", required = true) @NotBlank private String prodId; @ApiModelProperty(value = "商品单价", required = true) @NotNull @Min(0) private Long unitPriceInCent; } } 
  • 方法出参DTO

    • 没DTO的无需标记

    • 有DTO的 ,在DTO里用 @ApiModelProperty("…"),禁止用required属性

    • 由于响应参数无需校验参数,所以不带jsr303注解

    // 类上不需要标记 // 每个字段都必须标记,包括嵌入字段,如这里的 prodInfoList // 用 @ApiModelProperty("..."),不要用required @Data public class OrderRespDTO { @ApiModelProperty("生成的订单编号") private String orderNo; @ApiModelProperty("订单总价") private Long priceInCent; @ApiModelProperty("备忘(用户无备忘的时候为空)") private String memo; @ApiModelProperty("产品信息列表") private List<ProdInfo> prodInfoList; @Data public static class ProdInfo { @ApiModelProperty("商品ID") private String prodId; @ApiModelProperty("商品单价") private Long unitPriceInCent; } } 
2.3 补充其他 2.3.1 DTO使用规范
  • 不是每个方法都需要将入参封装成DTO类
  • 传JSON必须使用DTO类
  • 传键值对,参数比较少的,不要用DTO类,直接写在控制器的方法里列出
  • 传键值对,如果字段比较多,允许使用DTO类,但不建议,直接平铺到控制器方法更加直观
  • 返回值要封装统一的DTO,如ResponseDTO,一般有code、message、reqTime、data字段
  • 控制器的方法返回值,即丢在ResponseDTO里的data,如果有返回值,建议data使用DTO装起来,例如将UserDTO放在data里
  • 如果控制器的方法返回值ResponseDTO里的data比较简单,也允许使用单一类型
  • DTO分为reqDTO和respDTO,分别对应入参和出参,一般是一对的,不建议DTO继承,禁止控制器不同方法复用同一个DTO,禁止DTO里存在没用的字段
  • DTO有符合结构的,建议使用共有的静态内部类,写在同一个文件里比较好找
  • DTO使用lombok,以方便阅读
2.3.2 为什么会有上面的规范

上面的Swagger使用规范是根据实际情形制定出来的。首先使用繁琐将不利于推广,大浪淘沙后,就留下了上述少量的注解。

  • required=…在可以省略的情况下,即使顶着 “IDEA 提示 redundant” 还要坚持 “禁止省略”,为什么?

    为了风格统一,为了直观。否则还需要在脑里反应一会,才知道其默认值。尤其是 @RequestParam默认是true,但是@ApiParam的required默认false,容易搞混

  • 有些地方一定要用 @RequestParam,有些一定不要用,为什么?

    因为 Swagger有bug,对于单一类型的字段,不写会导致它认为是传JSON,但是对于DTO类型的入参,写上之后展不开DTO里面的字段值

  • 要求返回值DTO不要用 @ApiModelProperty的required属性,为什么?

    因为没有意义,不增加开发者的负担。又不是入参,必不必填不重要。如果说你用required表示字段会不会出现,这用法不好。写在DTO里的字段,即使它的值是null,在JSON格式化之后也是定会出现的;如果你说用required表示这个值会不会是null,建议真别这么增加后端工作量,代码一改就变还要维护这个属性值

  • 要求入参DTO不要用example,即 @ApiModelProperty(example="…", required=…),为什么?

    • example的属性,本意是参数示例值,这跟属性的设计不一致

    • 在DTO,名叫A,里面有个List的字段,B是一个类,如果在此字段使用 @ApiModelProperty(example="…", required=…),将不能展开B类里的字段

    • 使用后,提交接口,对于Integer类型的,也会显示成字串,提交失败

    • 会抛出错误,例如遇到下面的用法时(抛出错误不影响使用,但是会在控制台打印出来)

      // 这个明明是数字类型,example却写了字串,会导致AbstractSerializableParameter#getExample() // 抛出格式化错误的异常 @ApiModelProperty(example = "售价", required = true) private Integer priceInCent; 

Example

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3. Swagger其他 3.1 如何更换UI

我们知道使用Swagger需要引入两个GAV,其中UI的那个是可以替换的。新UI不仅仅是UI上的改变,还有导出md等重要功能。

官方的是io.springfox:springfox-swagger-ui,可以换成别的,注意不要同时存在多个UI,例如

 <dependency> <groupId>com.github.xiaoymin <dependency> <groupId>com.github.caspar-chen if (example == null) { return null; } try { if (BaseIntegerProperty.TYPE.equals(type)) { return Long.valueOf(example); } else if (DecimalProperty.TYPE.equals(type)) { return Double.valueOf(example); } else if (BooleanProperty.TYPE.equals(type)) { if ("true".equalsIgnoreCase(example) || "false".equalsIgnoreCase(defaultValue)) { return Boolean.valueOf(example); } } } catch (NumberFormatException e) { LOGGER.warn(String.format("Illegal DefaultValue %s for parameter type %s", defaultValue, type), e); } return example; } 

解决方法: 换JAR包,详细见下面的搭建过程已经排除了这个问题了

需要注意的是,2.8.0版本的swagger,不会出现这个问题(经过实际测试了),2.8.0的代码也是`if (example == null)` 这么判断,为什么就不会出现这个问题呢? 暂时不知道为什么 @JsonProperty("x-example") public Object getExample() { if (example == null) { return null; } ... } 
3.5 必填参数显示不准确的问题

没办法解决

说的是这个问题

@ApiOperation("新增订单") @PostMapping("/order/add") public ResponseDTO<OrderRespDTO> addOrder( @ApiParam(value = "订单信息", required = true) @Valid @RequestBody OrderReqDTO orderReqDTO ) @Data public class OrderReqDTO { @NotEmpty @Valid @ApiModelProperty(value = "商品信息列表", required = true) private List<ProdInfo> prodInfoList; @NotNull @ApiModelProperty(value = "商品总价", required = true) private Long totalPriceInCent; @ApiModelProperty(value = "备注", required = false) private String memo; @Data public static class ProdInfo { @ApiModelProperty(value = "商品ID", required = true) @NotBlank private String prodId; @ApiModelProperty(value = "商品单价", required = true) @NotNull @Min(0) private Long unitPriceInCent; } } 

实际上可以看到 prodInfoList 里的 ProdInfo 的两个字段都被标注为必填,但是界面上却显示非必填

在这里插入图片描述 最初我以为是bootstrap-ui的问题,后来看官方UI也是这样子。 在这里插入图片描述

4. Springboot+Swagger2搭建步骤

演示了springboot结合swagger2的

  1. pom.xml 中添加(尽量用新版本)
  <dependency> <groupId>io.springfox  <dependency> <groupId>io.springfox     
  1. 写一个 Swagger2Config 配置类(详细见后面的附录)
1)标注:@Configuration 和 @EnableSwagger2
2)产生一个 Docket 的Bean
  1. 启动springboot并访问:http://{host}:{port}/swagger-ui.html(使用的ui不同则不同)
  2. 附录:Swagger2Config 类代码,需要修改basePackage,扫描注解产生文档
@Configuration @EnableSwagger2 public class Swagger2Config { @Bean public Docket docket() { // basePackage 需要扫描注解生成文档的路径 return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.wyf.test.swagger2springboot")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("Swagger demo") .description("这是展示Swagger怎么用的例子") .version("1.0").build(); } } 
关注
打赏
1688896170
查看更多评论

暂无认证

  • 6浏览

    0关注

    115984博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文
立即登录/注册

微信扫码登录

0.0461s