SpringBoot——统一接口返回值和全局异常处理

统一接口返回值

1、定义通用的数据返回对象

@Data
@Accessors(chain = true)
public class GlobalResponse<T> implements Serializable {
    private Integer code;
    private String message;
    private T data;
    private final String timeStamp = DateUtils.localDateTimeToString(LocalDateTime.now());

    public static final String SUCCESS = "成功";
    public static final String FAILURE = "失败";


    public GlobalResponse(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public GlobalResponse(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public static GlobalResponse<Object> success() {
        return new GlobalResponse<Object>(200, SUCCESS);
    }

    public static GlobalResponse<Object> success(Object data) {
        return new GlobalResponse<Object>(200, SUCCESS, data);
    }

    public static GlobalResponse<Object> failure() {
        return new GlobalResponse<Object>(400, FAILURE);
    }

    public static GlobalResponse<Object> failure(Object data) {
        return new GlobalResponse<Object>(400, FAILURE, data);
    }

    public static GlobalResponse<Object> failure(Object data, String sign) {
        return new GlobalResponse<Object>(400, FAILURE, data);
    }

}

GlobalResponse:这里我们定义通用的数据返回对象,并且提供一个正常数据返回/一个异常数据返回的快速设置方法。

统一返回值和全局异常处理

/**
 * 自定义异常
 */
public class GlobalException extends RuntimeException {
    public GlobalException() {
    }

    public GlobalException(String message) {
        super(message);
    }

    public GlobalException(String message, Throwable t) {
        super(message, t);
    }

}

设置注解

/**
 * ResponseBody自定义注解,修饰在controller或者他的方法之上
 * 如果修饰在controller之上,它所有的请求返回都需要组织自定义返回报文;
 * 如果只修饰在方法上,该方法的请求返回都需要组织自定义返回报文;
 * 如果没有修饰,则直接返回
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@ResponseBody
public @interface ResponseResultBody {

}

第二步:定义统一处理handler

异常拦截 implements ResponseBodyAdvice

import com.zsn.scheduler.annotation.ResponseResultBody;
import com.zsn.scheduler.exception.GlobalException;
import com.zsn.scheduler.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.annotation.Annotation;
import java.util.List;
/**
 * 异常拦截
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler implements ResponseBodyAdvice<Object> {
    private static final Class<? extends Annotation> ANNOTATION_TYPE = ResponseResultBody.class;

    @ExceptionHandler(value = GlobalException.class)
    public GlobalResponse<Object> globalException(GlobalException e) {
        e.printStackTrace();
        return GlobalResponse.failure(e.getMessage());
    }

    @ExceptionHandler(value = Exception.class)
    public GlobalResponse<Object> exception(Exception e) {
        e.printStackTrace();
        return GlobalResponse.failure(e.getMessage());
    }

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public GlobalResponse<Object> parameterExceptionHandler(MethodArgumentNotValidException e) {
        e.printStackTrace();
        BindingResult bindingResult = e.getBindingResult();
        if (bindingResult.hasErrors()) {
            List<ObjectError> errors = bindingResult.getAllErrors();
            FieldError fieldError = (FieldError) errors.get(0);
            log.warn("object name is " + fieldError.getObjectName());
            log.warn("defaultMessage is " + fieldError.getDefaultMessage());
            log.warn("field is" + fieldError.getField());
            return GlobalResponse.failure(fieldError.getDefaultMessage());
        } else {
            return GlobalResponse.failure("参数绑定未知错误");
        }
    }

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return AnnotatedElementUtils.hasAnnotation(methodParameter.getContainingClass(), ANNOTATION_TYPE) || methodParameter.hasMethodAnnotation(ANNOTATION_TYPE);
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        if (o instanceof GlobalResponse) {
            return o;
        }
        if (o instanceof String) {
            //JsonUtils转换,使用的是ObjectMapperd对象,进行封装
            //new ObjectMapper().writeValueAsString(GlobalResponse.success(o));
            return JsonUtils.object2JsonString(GlobalResponse.success(o));
        }
        return GlobalResponse.success(o);
    }
}

@RestControllerAdvice+@ExceptionHandler配合使用
首先,我们通过@RestControllerAdvice来定义一个controller增强处理器,可以通过配合使用@ExceptionHandler来进行异常的统一处理。
其次,通过实现ResponseBodyAdvice,对于数据的返回,进行进一步的处理,使得接口的返回值都是统一的对象。

  • supports 方法作用

supports方法是通过判断,是否执行beforeBodyWrite。supports返回ture表示执行,supports返回false表示不执行。

supports中的returnType可以获取到controller的类,参数和方法等。

  • beforeBodyWrite作用

通过方法名称可以看出该方法是在写body之前处理操作。可以根据这些参数判断是否需要改写body,以及改写成什么内容。

你可能感兴趣的:(Spring,boot,工具篇,笔记,java,开发语言,后端)