统一封装Json和异常处理
ApiStatus
org.springframework.http.HttpStatus 没办法使用 @JsonValue 注解, 所以封装一层
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;
/**
* @author ZJ.Fang
* @date 2023/4/13 10:29
*/
@Getter
@AllArgsConstructor
public enum ApiStatus {
OK(HttpStatus.OK.value(), "请求成功"),
BAD_REQUEST(HttpStatus.BAD_REQUEST.value(), "参数异常"),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务器内部异常");
@JsonValue
private final Integer value;
private final String desc;
}
ApiResult
统一返回的数据格式
import lombok.Getter;
import lombok.Setter;
/**
* @author ZJ.Fang
* @date 2023/4/13 9:56
*/
@Getter
@Setter
public class ApiResult<T> {
private ApiStatus code;
private String msg;
private T data;
public static <T> ApiResult<T> success(T data) {
ApiStatus status = ApiStatus.OK;
return new ApiResult<>(status, status.getDesc(), data);
}
public static <T> ApiResult<T> failure(ApiStatus status, String msg) {
return new ApiResult<>(status, msg);
}
public static <T> ApiResult<T> failure(ApiStatus status, String msg, T t) {
return new ApiResult<>(status, msg, t);
}
/**
* 私有化构造方法
*/
private ApiResult() {
}
private ApiResult(ApiStatus status, String msg) {
this.code = status;
this.msg = msg;
}
private ApiResult(ApiStatus status, String msg, T data) {
this.code = status;
this.msg = msg;
this.data = data;
}
}
ApiException
import lombok.Getter;
import lombok.Setter;
/**
* API异常基类
* @author ZJ.Fang
*/
@Getter
@Setter
public class ApiException extends RuntimeException {
private ApiStatus status;
public ApiException(ApiStatus status, String message) {
super(message);
this.status = status;
}
public ApiException(ApiStatus status, String message, Throwable cause) {
super(message, cause);
this.status = status;
}
}
ApiParamException
/**
* 参数异常
* @author ZJ.Fang
*/
public class ApiParamException extends ApiException {
public ApiParamException(String message) {
super(ApiStatus.BAD_REQUEST, message);
}
public ApiParamException(String message, Throwable cause) {
super(ApiStatus.BAD_REQUEST, message, cause);
}
}
ResponseAdvice
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* @author ZJ.Fang
* @date 2023/4/13 10:00
*/
@Slf4j
@ControllerAdvice
public class MyResponseAdvice implements ResponseBodyAdvice<Object> {
/**
* 这个地方如果返回false ,不会执行 beforeBodyWrite 方法
*/
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
Class<?> type = returnType.getParameterType();
return type != ApiResult.class;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
return ApiResult.success(body);
}
@ResponseBody
@SuppressWarnings("unused")
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public ApiResult<Object> handleException(HttpServletRequest request, Exception ex) {
log.error(ex.getMessage(), ex);
return ApiResult.failure(ApiStatus.INTERNAL_SERVER_ERROR, ex.getMessage(), getStackTrace(ex));
}
/**
* 异常堆栈信息拼接
* @param exception 异常
* @return 堆栈信息
*/
private String getStackTrace(Exception exception) {
String info = String.format("%s:%s%n", exception.getClass().getName(), exception.getMessage());
StringBuilder msg = new StringBuilder(info);
StackTraceElement[] stackTrace = exception.getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
StringBuilder fileName = new StringBuilder();
if (stackTraceElement.getFileName() != null) {
fileName.append(stackTraceElement.getFileName());
if (stackTraceElement.getLineNumber() > 0) {
fileName.append(":").append(stackTraceElement.getLineNumber());
}
} else {
fileName.append("Unknown Source");
}
msg.append(stackTraceElement.getClassName()).append(".").append(stackTraceElement.getMethodName()).append("(").append(fileName).append(")").append(String.format("%n"));
}
//返回信息
return msg.toString();
}
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(ApiException.class)
public ApiResult<Object> apiExceptionHandle(Exception ex) {
log.error(ex.getMessage(), ex);
ApiException apiException = (ApiException) ex;
return ApiResult.failure(apiException.getStatus(), apiException.getMessage());
}
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ApiParamException.class)
public ApiResult<Object> paramExceptionHandle(Exception ex) {
log.error(ex.getMessage(), ex);
ApiParamException apiException = (ApiParamException) ex;
return ApiResult.failure(apiException.getStatus(), appendParamMsg(apiException.getMessage()));
}
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(BindException.class)
public ApiResult<Object> bindExceptionHandle(Exception ex) {
String msg = getMsgByBindException((BindException) ex);
log.error(msg, ex);
return ApiResult.failure(ApiStatus.BAD_REQUEST, msg);
}
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
public ApiResult<Object> constraintViolationExceptionHandle(Exception ex) {
log.error(ex.getMessage(), ex);
return ApiResult.failure(ApiStatus.BAD_REQUEST, appendParamMsg(ex.getMessage()));
}
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResult<Object> methodArgumentNotValidExceptionHandle(Exception ex) {
log.error(ex.getMessage(), ex);
MethodArgumentNotValidException exception = (MethodArgumentNotValidException) ex;
String msg = getMsgByBindResult(exception.getBindingResult());
return ApiResult.failure(ApiStatus.BAD_REQUEST, appendParamMsg(msg));
}
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MissingServletRequestParameterException.class)
public ApiResult<Object> missingServletRequestParameterExceptionHandle(MissingServletRequestParameterException exception) {
log.error(exception.getMessage(), exception);
return ApiResult.failure(ApiStatus.BAD_REQUEST, appendParamMsg(exception.getMessage()));
}
private String getMsgByBindException(BindException bindException) {
BindingResult bindingResult = bindException.getBindingResult();
return getMsgByBindResult(bindingResult);
}
private static String getMsgByBindResult(BindingResult bindingResult) {
List<ObjectError> errors = bindingResult.getAllErrors();
return errors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","));
}
private String appendParamMsg(String msg) {
return ApiStatus.BAD_REQUEST.getDesc()+ "," + msg;
}
}