前言
前后端分离开发,经常用json数据格式进行交互,统一的响应数据格式,形成统一规范,可以大大方便前后端人员的开发。
Response数据封装
- 编码枚举类 ResultCode.java
public enum ResResultCode {
/**
* 成功
*/
SUCCESS(200, "ok"),
/**
* 请求错误
*/
BAD_REQUEST(400, "请求参数错误"),
/**
* 未登录授权
*/
UNAUTHORIZED(401, "无效令牌"),
/**
* 权限不足
*/
FORBIDDEN(403, "权限不足"),
/**
* 服务器错误
*/
FAILED(500, "server error"),
/**
* 业务异常
*/
BIZ_ERROR(600, "业务异常");
private int status;
private String message;
ResResultCode(int status, String message) {
this.status = status;
this.message = message;
}
public int getStatus() {
return status;
}
public String getMessage() {
return message;
}
public void setStatus(int status) {
this.status = status;
}
public void setMessage(String message) {
this.message = message;
}
}
- json数据封装类
@Data
public class ResResult {
/**
* 响应编码
*/
private int code;
/**
* 响应信息
*/
private String msg;
/**
* 响应数据
*/
private Object data;
public ResResult(int code, String msg) {
this.code = code;
this.msg = msg;
}
public ResResult(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static ResResult success() {
return new ResResult(ResResultCode.SUCCESS.getStatus(), ResResultCode.SUCCESS.getMessage(), null);
}
public static ResResult success(Object data) {
return new ResResult(ResResultCode.SUCCESS.getStatus(), ResResultCode.SUCCESS.getMessage(), data);
}
public static ResResult failure() {
return new ResResult(ResResultCode.FAILED.getStatus(), ResResultCode.FAILED.getMessage());
}
public static ResResult failure(ResResultCode ResResultCode) {
return new ResResult(ResResultCode.getStatus(), ResResultCode.getMessage());
}
}
自定义异常类
- BizErrorException.java 用来抛出业务异常
public class BizErrorException extends RuntimeException {
private ResResultCode resResultCode;
private String message;
public BizErrorException(ResResultCode resResultCode) {
this.resResultCode = resResultCode;
}
public BizErrorException(ResResultCode resResultCode, String message) {
this.resResultCode = resResultCode;
this.message = message;
}
public ResResultCode getResResultCode() {
return resResultCode;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
- BadRequestException.java 用来抛出请求参数异常
public class BadRequestException extends RuntimeException {
public BadRequestException() {
}
public BadRequestException(String message) {
super(message);
}
}
全局请求参数校验
虽然spring提供了@Valid可以进行请求参数的校验
注意一个坑:hibernate-validator在springboot 2.3之后就需要手动引入了,不再自动配置。
org.springframework.boot
spring-boot-starter-validation
作为一个前后端分离项目,并且接口并不对外暴露,后端每个接口调用都创建一个vo类,显然是非常麻烦的,这边就不用这种方式。
这里就选择了一个偷懒的方法,就是利用Knife4j
动态请求参数 再加上AOP 进行校验。具体实现步骤:
- 引入AOP依赖
org.springframework.boot
spring-boot-starter-aop
- 新建一个aop类 ReqValidateAop.java
在执行每个@DynamicParameters注解修饰的接口是进行校验,对每个required = true 的参数进行校验。
/**
* 对含有@DynamicParameters动态参数的接口 进行请求参数校验
*/
@Aspect
@Component
@Slf4j
public class ReqValidateAop {
/**
* 所有方法: @Pointcut("execution(* com.ify.formwork.web.controller.*.*.*(..))")
* 含有@DynamicParameters注解:@Pointcut("@annotation(com.github.xiaoymin.knife4j.annotations.DynamicParameters)")
*/
@Pointcut("@annotation(com.github.xiaoymin.knife4j.annotations.DynamicParameters)")
public void paramValidate() {
}
@Before("paramValidate()")
public void before(JoinPoint joinPoint) throws BadRequestException {
List errList = new ArrayList<>();
Object[] objects = joinPoint.getArgs();
JSONObject param = (JSONObject) objects[0];
// 获取当前方法
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
// 获取方法@DynamicParameters properties
DynamicParameters annotation = method.getAnnotation(DynamicParameters.class);
List list = Arrays.asList(annotation.properties());
for (DynamicParameter dynamicParameter : list) {
// 必传参数校验
if (dynamicParameter.required()) {
Object o = param.get(dynamicParameter.name());
// 请求参数为null 或请求参数为字符串为空串
boolean badRequest = (null == o) || (o instanceof String && StrUtil.isEmpty((String) o));
if (badRequest) {
errList.add(dynamicParameter.name() + "不能为空");
}
}
}
if (!errList.isEmpty()) {
throw new BadRequestException(StrUtil.join(",", errList));
}
}
}
全局异常拦截处理
对服务抛出的异常进行接管,统一处理返回给前端
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/***
* 功能描述: 捕获请求参数异常 ReqValidateAop.java 自定义校验方式
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(BadRequestException.class)
public ResResult handleBadRequestException(BadRequestException e) {
return new ResResult(ResResultCode.BAD_REQUEST.getStatus(), e.getMessage());
}
/**
* 功能描述: 捕获请求参数异常 @valid校验参数方式 (如果采用这种方式的话可以进行使用)
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResResult handleBadRequestException(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
List errors = new ArrayList<>();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
errors.add(fieldError.getField() + fieldError.getDefaultMessage());
}
return new ResResult(ResResultCode.BAD_REQUEST.getStatus(), StrUtil.join("|", errors));
}
/**
* 业务异常捕获 统一返回500状态,具体信息封装在msg里
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(BizErrorException.class)
public ResResult handleBizErrorException(BizErrorException e) {
if (StrUtil.isNotEmpty(e.getMessage())) {
e.getResResultCode().setMessage(e.getMessage());
}
return ResResult.failure(e.getResResultCode());
}
/**
* 功能描述: 异常统一返回, 统一返回500状态
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public ResResult handleException(Exception e) {
log.error("server error = {}", e.getMessage());
return ResResult.failure();
}
}
后端的统一数据封装差不多就这样了。。。