就像很多事物被大众滥用违背了事物创作者的初衷, 我也滥用了swagger, 不仅给自己也给前端的同事带来了不必要的麻烦和沟通的成本.
所以, 今天写下该博客分享下自己认为的swagger2+springBoot的最佳实践, 如何正确地使用swagger来达到替代传统接口文档和解决传统文档的痛点的两个目的.
传统接口文档的痛点如下:
1.实际代码和接口文档分离, 所以导致的一个问题就是代码的接口已经变动但接口文档还是n年前的, 实际接口与接口文档严重不一致.
2.后端每次改动接口都要去修改接口文档, 导致效率低下, 非常不爽.
3.对于响应数据的的格式无法尽可能地全面地进行说明, 因为对象中可能包含n个属性, n个属性中的某一个属性又包含一个对象, 所以手写很不现实.
除了第三点swagger解决的不是很完善很完美之外彻底解决了以上的痛点, 那么该如何正确使用swagger以得到这种好处呢?
这是我写的关于使用swagger的规范
/** * swagger使用规范 * 之所以设定规范是为了能够让swagger被正确运用以便达到替代传统接口文档和解决传统文档的痛点的两个目的. * * 传统接口文档中对于每个请求参数都有详细的描述, 前端对于多数请求参数都能准确理解, 但是在编写这份使用规范前swagger未被正确运用 * 已造成前端无法准确理解请求参数的含义,增加了不必要的沟通成本. * * 传统接口文档对于每个模块之间的分类很明确, 前端能够快速查找到对应模块的接口文档, 但是在编写这份使用规范前swagger未被正确运用 * 导致每个模块名称仅被描述为"xxx-controller",前端无法快速查找到对应模块的接口文档同时也增加了不必要的沟通成本. * * 传统接口文档对于请求响应都有code来表明当前请求的状况, 可根据对应的code来完成相应操作, 但是在编写这份使用规范前swagger未被正确运用, 导致前端无法准确理解目前所使用的Http code所代表的含义, 从而增加了不必要的沟通成本. * * 为了解决以上三个存在的问题从而达到易维护和消除不必要的沟通成本的目的, 请各位同事严格遵守以下规范. * 若有不同的想法,欢迎提出讨论并完善该规范. * * 1.controller中对于path及form类型参数的使用规范 * 请查看示例{@link com.hikedu.backend.swaggerexample.ExampleController#pathAndFormParamsStandard(String)} * * 2.controller中对于code的使用及描述规范 * 使用Http code并通过注解@ApiResponse来完成描述, 并根据对应的情况在ResponseEntity对象中返回. * 请查看示例中的@Responses注解的用法 * {@link com.hikedu.backend.swaggerexample.ExampleController#pathAndFormParamsStandard(String)} * * 3.禁止直接使用Model来作为controller的接收参数, 而是创建对应的DTO来接收参数. * 之所以禁止是因为如下几个原因: * 1.model的意义是在controller、service、dao、View层来传递数据, 所以尽量保证model功能的单一, 以避免产生不必要的复杂度. * 2.使用model因为存在诸如createdAt, updatedAt等不必要的字段并不适合作为controller的入参. * 3.model因其本身的意义不能添加太多和model功能不相干的属性以达到使用model作为controller参数的目的. * 因此有必要创建DTO来作为controller的参数. * 使用详见{@link com.hikedu.backend.model.dto.ExampleDTO} * * 4.controller中对于使用对象接收参数的使用规范 * 在当前规范未实施前, 所有的以对象接收的参数的字段含义对于前端来讲是模糊不清的. * 因此有必要对对象中的每个属性做描述,以便前端能够理解参数的含义. * 示例见{@link com.hikedu.backend.model.dto.ExampleDTO}中的参数注解 * 和{@link com.hikedu.backend.swaggerexample.ExampleController#objectParamsStandard(ExampleDTO)} * * 5.响应的model数据添加说明 * 未实施当前规范前, 所有的响应数据均没有说明, 前端无法理解响应数据中参数的含义, 因此有必要在响应的model中为每个参数添加说明 * 示例见如下两处 * {@link com.hikedu.backend.model.dto.ExampleResponseDataDTO} * {@link ExampleController#responseDataStandard()} * * @author himly z1399956473@gamil.com 2018.8.20 */
这是规范中所引用的ExampleDTO
/**
* dto示例
* 以用户注册账号为例
* @author himly z1399956473@gmail.com
*/
@Data
@ApiModel
public class ExampleDTO {
/**
* message请使用国际化配置文件中的key
* 此处方便展示直接使用中文.
*/
@ApiModelProperty(notes = "用户名, 不能为空, 否则后端抛出异常")
@NotNull(message = "用户名不能为空")
private String username;
@ApiModelProperty(notes = "密码, 密码不能为空且不能为简单密码, 否则后端抛出异常")
@NotNull(message = "密码不能为空")
private String password;
@ApiModelProperty(notes = "当前注册用户所属的国家名称, 不能为空, 否则后端抛出异常")
@NotNull(message = "所属国家不能为空")
private String country;
}
这是规范中所引用的ExampleController
/**
* @author himly z1399956473@gmail.com
*/
@RestController
@RequestMapping("/swagger-examples")
@Api(tags = "swagger使用规范", description = "用于展示swagger使用规范的接口列表")
public class ExampleController {
@ApiOperation(value = "url及form参数使用规范", response = ResponseEntity.class)
@ApiResponses({
//http code
@ApiResponse(code = HttpServletResponse.SC_OK, message = "成功"),
@ApiResponse(code = HttpServletResponse.SC_CONFLICT, message = "当前用户没有做到swagger使用规范")
})
@GetMapping(value = "/path-and-form-params-standard/{path}")
@ApiImplicitParams({
@ApiImplicitParam(name = "path", value = "路径参数",required = true, format = "path"),
@ApiImplicitParam(name = "form", value = "表单参数", required = true, format = "form")
})
public ResponseEntity pathAndFormParamsStandard(String form) {
boolean complianceWithTheSpecifications = false;
if (complianceWithTheSpecifications) {
return new ResponseEntity(HttpStatus.OK);
}else {
return new ResponseEntity(HttpStatus.CONFLICT);
}
}
@ApiOperation(value = "使用对象接收参数的使用规范", response = ResponseEntity.class)
@ApiResponses({
//http code
@ApiResponse(code = HttpServletResponse.SC_OK, message = "成功"),
@ApiResponse(code = HttpServletResponse.SC_CONFLICT, message = "当前用户没有做到swagger使用规范")
})
@GetMapping(value = "/object-params-standard")
public ResponseEntity objectParamsStandard(@Valid @RequestBody ExampleDTO exampleDTO) {
if (exampleDTO.getCountry() == null) {
return new ResponseEntity(HttpStatus.OK);
}else {
return new ResponseEntity(HttpStatus.CONFLICT);
}
}
/**
*swagger还是无法针对响应数据做出准确的描述, 因为ApiOperation中只能存在一个Model
* 但是在实际接口中, 可能会存在List<ExampleResponseDataDTO>或Map<String, ExampleResponseDataDTO>
* 及ResponseEntity<ExampleResponseDataDTo, OK>等数据格式.
*
* 无法准确地对每个参数做出描述, 因此在ApiOperation注解中的response属性设置为真正返回的类, 并在该类中对每个属性使用
* ApiProperty注解完成说明.
*
* @return response entity
*/
@ApiOperation(value = "swagger响应数据使用规范", response = ExampleResponseDataDTO.class)
@ApiResponses({
//http code
@ApiResponse(code = HttpServletResponse.SC_OK, message = "成功"),
@ApiResponse(code = HttpServletResponse.SC_CONFLICT, message = "当前用户没有做到swagger使用规范")
})
@GetMapping(value = "/response-data-standard")
public ResponseEntity<ExampleResponseDataDTO> responseDataStandard() {
ExampleResponseDataDTO responseDataDTO = new ExampleResponseDataDTO();
responseDataDTO.setName("test");
responseDataDTO.setTheJoinedDepartmentId(3L);
responseDataDTO.setUniversityId(3L);
return new ResponseEntity<>(responseDataDTO, HttpStatus.OK);
}
}
这是规范中所引用中的ExampleResponseDataDTO
/**
* 示例类
* @author himly z1399956473@gmail.com
*/
@Data
@ApiModel
public class ExampleResponseDataDTO {
@ApiModelProperty(notes = "用户名")
private String name;
@ApiModelProperty(notes = "用户所在的部门id")
private Long theJoinedDepartmentId;
@ApiModelProperty(notes = "用户所在的学校id")
private Long universityId;
}
最后在swagger-ui的呈现效果如下
note: @Data注解是lombok库中的注解, 不懂可以直接google.
代码就不传了, 对于如何合理的对于响应数据添加描述还是没有在swagger的文档中找到相关信息, 如果各位谁知道请一定要评论告知我.