全局统一返回+全局异常处理
承灿 2023/12/21
# 全局统一返回+全局异常处理
package com.example.commonresult.result;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
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.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* 定义全局性的控制器
* basePackages 作用范围
* @author lichengcan
*/
@RestControllerAdvice(basePackages = "com.dataojo.haikangbasic")
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
private static final Logger LOG = LoggerFactory.getLogger(ResponseAdvice.class);
/**
* 环境变量
*/
@Value("${spring.profiles.active}")
private String env;
/**
* 是否需要进行统一返回封装
*/
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
//定义到类级别:如果类上有这个注解,那就封装
if (returnType.getDeclaringClass().getAnnotation(ResponseResult.class)!=null) {
return true;
}
//也可以定义到方法级
// if (returnType.getMethodAnnotation(ResponseResult.class)!=null) {
// return true;
// }
return false;
}
/**
* 此方法才是真正的统一返回封装逻辑
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
// 提供一定的灵活度,如果body已经被包装了,就不进行包装
if (body instanceof Result || body instansof String) {
return body;
}
//包装类型
return Result.success(body);
}
/**
* 处理参数校验失败异常
*/
@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
BindingResult bindingResult = ex.getBindingResult();
String defaultMessage = bindingResult.getAllErrors().get(0).getDefaultMessage();
return buildErrorResponse(ResultEnum.SYSTEM_EXCEPTION.getCode(), defaultMessage);
}
/**
* 处理 Request Valid 异常
*/
@ResponseBody
@ExceptionHandler(BindException.class)
public Result handleBindException(BindException ex) {
String defaultMessage = ex.getAllErrors().get(0).getDefaultMessage();
return buildErrorResponse(ResultEnum.SYSTEM_EXCEPTION.getCode(), defaultMessage);
}
/**
* 处理 BeanInstantiationException 异常
*/
@ResponseBody
@ExceptionHandler(BeanInstantiationException.class)
public Result handleBeanInstantiationException(BeanInstantiationException ex) {
Throwable cause = ex.getCause();
return buildErrorResponse(ResultEnum.SYSTEM_EXCEPTION.getCode(), cause.getMessage());
}
/**
* 处理所有其他异常
*/
@ResponseBody
@ExceptionHandler(Exception.class)
public Result handleException(Exception ex) {
LOG.error("系统异常", ex);
String message;
if (ex instanceof HttpMessageNotReadableException) {
message = "请求参数格式错误";
} else if (ex instanceof HttpRequestMethodNotSupportedException) {
message = "不支持的请求方式";
} else {
message = "系统繁忙,请稍后再试";
}
return buildErrorResponse(ResultEnum.SYSTEM_EXCEPTION.getCode(), message);
}
/**
* 构建错误响应
*/
private Result buildErrorResponse(String code, String message) {
if ("dev".equals(env)) {
return Result.failed(code, message);
} else {
return Result.failed(code, null);
}
}
}
# 自定义注解
package com.example.commonresult.result;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 统一返回注解
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ResponseResult {
}
# 统一返回数据结构
package com.example.commonresult.result;
import lombok.Data;
/**
* 统一返回数据结构
*
* @param <T>
*/
@Data
public class Result<T> {
private String code;
private String message;
private T data;
public Result() {
}
public Result(String code, String message) {
this.code = code;
this.message = message;
}
public Result(String code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static <T> Result<T> success(T data) {
return new Result<>(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), data);
}
public static Result<?> failed(String code, String message) {
return new Result<>(code, message);
}
}
# 枚举
package com.example.commonresult.result;
/**
* 常用结果的枚举
*/
public enum ResultEnum {
SUCCESS("00000", "请求成功"),
VALIDATE_FAILED("2002", "参数校验失败"),
COMMON_FAILED("2003", "接口调用失败"),
FORBIDDEN("2004", "没有权限访问资源"),
USER_EXCEPTION("A0001", "用户端错误"),
SYSTEM_EXCEPTION("B0001", "系统执行出错"),
THIRD_PARTY_SERVICE_INVOKE_EXCEPTION("C0001", "调用第三方服务出错");
private String code;
private String message;
// 构造方法
ResultEnum(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
}
# 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- alibaba fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<!-- spring data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
# 测试
package com.example.commonresult.controller;
import com.example.commonresult.result.ResponseResult;
import com.example.commonresult.result.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author: lichengcan
* @date: 2023-09-07 15:23
* @description
**/
@RestController
@RequestMapping("/test")
@ResponseResult
public class TestController {
@GetMapping("/test")
public Object test(Integer num){
Map map = new HashMap();
int j = 10/num;
map.put(1,1);
return Result.success(map);
}
@GetMapping("/list")
public List<Integer> list(Integer num){
List<Integer> list = new ArrayList<>(num);
for (int i = 0; i < num; i++) {
list.add(i);
}
return list;
}
@GetMapping("/exception")
public void exception(Integer num){
int j = 10/num;
}
@GetMapping("/testString")
public String testString(Integer num){
return "李承灿大帅逼";
}
@GetMapping("/testInteger")
public Integer testInteger(Integer num){
return num;
}
}