1.Knife4j框架
Knife4j框架是一款基于Swagger 2框架的、能够基于项目中的控制器的代码来生成在线API文档的框架,另外,此框架还有调试功能,可以向服务器端发送请求,并获取响应结果。
关于此框架,要使之能够使用,需要:
- 添加依赖
- 添加配置类
- 在
application.properties 中添加1条配置
关于依赖的代码:
<!-- Knife4j Spring Boot:在线API -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.9</version>
</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
注意:
- 当前项目的Spring Boot版本必须是2.6以下的版本(2.6不可用)
- 如果要使用更高版本的Spring Boot,必须使用更高版本的Knife4j
- 在配置类中的
basePackage 必须是控制器类所在的包,记得需要修改
完成后,启动项目,通过?/doc.html ?即可访问在线API文档。
在开发实践中,还应该对在线API文档进行细化,需要在控制器及相关类中进行一些配置:
- 在控制器类上添加
@Api 注解,配置tags 属性,此属性是String 类型的 - 在控制器类中处理请求的方法上添加
@ApiOperation 注解,配置value 属性,此属性是String 类型的 - 在控制器类中处理请求的方法上添加
@ApiOperationSupport 注解,配置order 属性,此属性是int 类型的
- 此属性用于排序,数据越小越靠前,不建议使用1位的数字
- 在控制器类中处理请求的方法上,不要再使用没有限制请求方式的
@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;
}
2.关于响应结果
当服务器端向客户端响应数据时,除了必要的提示文本以外,还应该响应“业务状态码”到客户端,以便于客户端程序能够便捷且准确的判断当前请求的执行结果!
另外,某些操作是需要向客户端响应数据的
所以,向客户端的响应数据至少需要包含以下部分:
- 业务状态码:本质上是一个数值,由服务器端和客户端共同约定每个数值的意义
- 业务执行出错时的描述文本
- 数据:当处理请求成功时,可能需要响应某些数据到客户端(通常是客户端发起GET请求,当然,某些POST请求可能也需要响应数据)
以上做法应该是针对所有请求都是如此响应的,通常,会自定义某个类型,用于封装以上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 ,由于使用了统一处理异常的机制,会导致所有异常的业务状态码都是相同的!为了解决此问题,可以:
- 创建多种异常类型,针对不同的错误,在Service层抛出不同的异常
- 在Service层每次抛出异常时,向异常对象中封装业务状态码
如果采取以上第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
}
});
|