Knife4j框架是一款基于Swagger 2框架的、能够基于项目中的控制器的代码来生成在线API文档的框架,另外,此框架还有调试功能,可以向服务器端发送请求,并获取响应结果。
关于此框架,要使之能够使用,需要:
application.properties中添加1条配置关于依赖的代码:
- <dependency>
- <groupId>com.github.xiaoymingroupId>
- <artifactId>knife4j-spring-boot-starterartifactId>
- <version>2.0.9version>
- dependency>
关于配置类:
- import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- 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.EnableSwagger2WebMvc;
-
- /**
- * Knife4j配置类
- *
- * @author java@tedu.cn
- * @version 0.0.1
- */
- @Slf4j
- @Configuration
- @EnableSwagger2WebMvc
- public class Knife4jConfiguration {
-
- /**
- * 【重要】指定Controller包路径
- */
- private String basePackage = "cn.tedu.csmall.product.controller";
- /**
- * 分组名称
- */
- private String groupName = "product";
- /**
- * 主机名
- */
- private String host = "http://java.tedu.cn";
- /**
- * 标题
- */
- private String title = "酷鲨商城在线API文档--商品管理";
- /**
- * 简介
- */
- private String description = "酷鲨商城在线API文档--商品管理";
- /**
- * 服务条款URL
- */
- private String termsOfServiceUrl = "http://www.apache.org/licenses/LICENSE-2.0";
- /**
- * 联系人
- */
- private String contactName = "Java教学研发部";
- /**
- * 联系网址
- */
- private String contactUrl = "http://java.tedu.cn";
- /**
- * 联系邮箱
- */
- private String contactEmail = "java@tedu.cn";
- /**
- * 版本号
- */
- private String version = "1.0.0";
-
- @Autowired
- private OpenApiExtensionResolver openApiExtensionResolver;
-
- public Knife4jConfiguration() {
- log.debug("加载配置类:Knife4jConfiguration");
- }
-
- @Bean
- public Docket docket() {
- String groupName = "1.0.0";
- Docket docket = new Docket(DocumentationType.SWAGGER_2)
- .host(host)
- .apiInfo(apiInfo())
- .groupName(groupName)
- .select()
- .apis(RequestHandlerSelectors.basePackage(basePackage))
- .paths(PathSelectors.any())
- .build()
- .extensions(openApiExtensionResolver.buildExtensions(groupName));
- return docket;
- }
-
- private ApiInfo apiInfo() {
- return new ApiInfoBuilder()
- .title(title)
- .description(description)
- .termsOfServiceUrl(termsOfServiceUrl)
- .contact(new Contact(contactName, contactUrl, contactEmail))
- .version(version)
- .build();
- }
-
- }
关于application.properties中的配置:
- # 开启Knife4j框架的增强模式
- knife4j.enable=true
注意:
basePackage必须是控制器类所在的包,记得需要修改完成后,启动项目,通过 /doc.html 即可访问在线API文档。
在开发实践中,还应该对在线API文档进行细化,需要在控制器及相关类中进行一些配置:
@Api注解,配置tags属性,此属性是String类型的@ApiOperation注解,配置value属性,此属性是String类型的@ApiOperationSupport注解,配置order属性,此属性是int类型的
@RequestMapping,建议使用@GetMapping或@PostMapping@ApiModelProperty注解,以配置对参数的说明@ApiImplicitParams和@ApiImplicitParam这2个注解组合来配置
@ApiImplicitParam,原本的提示的值会被覆盖,应该完整的配置各属性完整的配置示例--AlbumController:
- @Api(tags = "04. 相册管理模块")
- @Slf4j
- @RestController
- @RequestMapping("/albums")
- public class AlbumController {
-
- @Autowired
- private IAlbumService albumService;
-
- public AlbumController() {
- log.info("创建控制器:AlbumController");
- }
-
- // 添加相册
- // http://localhost:9080/albums/add-new?name=XiaoMi&description=TestDescription&sort=69
- @ApiOperation("添加相册")
- @ApiOperationSupport(order = 100)
- @PostMapping("/add-new")
- public String addNew(@Validated AlbumAddNewDTO albumAddNewDTO) {
- log.debug("开始处理【添加相册】的请求:{}", albumAddNewDTO);
- albumService.addNew(albumAddNewDTO);
- return "添加相册成功!";
- }
-
- // http://localhost:9080/albums/9527/delete
- @ApiOperation("根据id删除相册")
- @ApiOperationSupport(order = 200)
- @ApiImplicitParams({
- @ApiImplicitParam(name = "id", value = "相册id", dataType = "long", required = true)
- })
- @PostMapping("/{id:[0-9]+}/delete")
- public String delete(@PathVariable Long id) {
- log.debug("开始处理【删除相册】的请求:id={}", id);
- albumService.deleteById(id);
- return "删除相册成功!";
- }
-
- }
完整的配置示例--AlbumAddNewDTO:
- @Data
- public class AlbumAddNewDTO implements Serializable {
-
- /**
- * 相册名称
- */
- @ApiModelProperty(value = "相册名称", example = "小米80的相册", required = true)
- @NotNull(message = "必须提交相册名称!")
- private String name;
-
- /**
- * 相册简介
- */
- @ApiModelProperty(value = "相册简介", example = "小米80的相册的简介", required = true)
- @NotNull(message = "必须提交相册简介!")
- private String description;
-
- /**
- * 自定义排序序号
- */
- @ApiModelProperty(value = "自定义排序序号", example = "88", required = true)
- @NotNull(message = "必须提交自定义排序序号!")
- @Range(max = 99, message = "自定义排序序号必须是0~99之间的值!")
- private Integer sort;
-
- }
当服务器端向客户端响应数据时,除了必要的提示文本以外,还应该响应“业务状态码”到客户端,以便于客户端程序能够便捷且准确的判断当前请求的执行结果!
另外,某些操作是需要向客户端响应数据的
所以,向客户端的响应数据至少需要包含以下部分:
以上做法应该是针对所有请求都是如此响应的,通常,会自定义某个类型,用于封装以上3种数据,作为处理请求的方法的返回值类型,当响应时,Spring MVC框架会将返回值转换成JSON格式的字符串!
提示:Spring MVC能够将处理请求的方法的返回值转换成JSON格式的字符串,需要:
- 此方法是响应正文的
- 此项目中需要添加
jackson-databind依赖
- 在Spring Boot中,
spring-boot-starter-web中包含了此依赖- 此方法的返回值类型在Spring MVC中没有默认的Converter(转换器),会自动调用
jackson-databind中的Converter,而jackson-databind的处理方法就是将返回值转换成JSON格式的字符串
- 只要是自定义的数据类型,在Spring MVC中都没有默认的Converter
例如,在项目的根包下创建web.JsonResult类:
- @Data
- public class JsonResult implements Serializable {
- private Integer state;
- private String message;
- private Object data;
- }
以上类型,将作为项目中每个处理请求的方法、每个处理异常的方法的返回值类型!
如果在Service层始终抛出ServiceException,由于使用了统一处理异常的机制,会导致所有异常的业务状态码都是相同的!为了解决此问题,可以:
如果采取以上第2种做法,则需要将ServiceException调整为:
- @Getter
- public class ServiceException extends RuntimeException {
-
- private Integer state;
-
- public ServiceException(Integer state, String message) {
- super(message);
- this.state = state;
- }
-
- }
然后,另外自定义一个接口,用于声明各业务状态码的常量:
- public interface ServiceCode {
-
- Integer ERR_CONFLICT = 2;
- Integer ERR_NOT_FOUND = 6;
- Integer ERR_INSERT = 3;
- Integer ERR_UPDATE = 4;
- Integer ERR_DELETE = 5;
-
- }
并且,在抛出异常时,向异常对象中封装以上业务状态码,例如:
- String message = "添加相册失败!相册名称【" + name + "】已存在!";
- log.warn(message);
- throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
- this.axios.post(url, data).then(() => {
- let data = response.data;
- if (data.state == 1) {
- // 成功
- } else if (data.state == 2) {
- // 显示 data.message
- }
- });