SpringBoot统一异常与接口响应

Springboot统一信息处理

现在我们的开发模式都是前后端分离的开发方式,前后端的信息交互是我们值得去关注的一个地方。我们采用SpringWeb中的ResponseBodyAdvice接口和@ControllerAdvice、@ExceptionHandler注解完成统一信息处理。

我们先介绍一下ResponseBodyAdvice接口我们看到了它是在控制器方法执行之后编写自定义响应的接口。由(请求映射处理程序适配器)和(异常处理程序异常解析器)匹配我们的自定义响应信息并配合@ControllerAdvice注解完成我们的自定义的统一信息返回.在这里插入图片描述
ResponseBodyAdvice接口中有supports()和beforeBodyWrite()两个方法,这两个方法是定义我们自定义接口响应的方法.
supports():该组件是否支持给定的控制器方法返回类型和选定的HttpMessageConverter类型。
beforeBodyWrite():在选择HttpMessageConverter之后,在调用它的写方法之前调用

下面就看是如何实现的吧!!!

1、统一异常处理
完成统一异常处理我们需要使用到@ControllerAdvice、@ExceptionHandler注解两个注解.
@ControllerAdvice注解是Spring MVC中一个全局的异常处理注解。它可以应用到类上,用来处理控制器中所有带有@RequestMapping注解的方法抛出的异常。这样可以避免在每个控制器中都进行重复的异常处理。
@ExceptionHandler注解是Spring框架中的一个注解,用于处理在Controller中抛出的异常。它可以指定一个方法来处理特定类型的异常,并将异常信息转换为HTTP响应.
1.1定义自定义异常类和异常枚举类

//自定义异常类
public class RestException extends RuntimeException {
    private RestResponse<String> restResponse;

    public RestException(Integer code, String desc){
        super(desc);
        this.restResponse = new RestResponse<>();
        this.restResponse.setCode(code);
        this.restResponse.setDescription(desc);
        this.restResponse.setResult(desc);
    }

    public RestException(RestErrorEnum errorEnum) {
        super(errorEnum.getDescription());
        this.restResponse = new RestResponse<>();
        this.restResponse.setCode(errorEnum.getCode());
        this.restResponse.setDescription(errorEnum.getDescription());
    }

    public RestException(RestErrorEnum errorEnum, String result) {
        super(result);
        this.restResponse = new RestResponse<>();
        this.restResponse.setCode(errorEnum.getCode());
        this.restResponse.setResult(result);
        this.restResponse.setDescription(errorEnum.getDescription());
    }

    public Integer getCode() {
        return this.restResponse.getCode();
    }

    public String getResult() {
        return this.restResponse.getResult();
    }

    public String getDescription() {
        return this.restResponse.getDescription();
    }
}
//自定义枚举类
public enum RestErrorEnum {
    SUCCESS(0, "成功"),

    //未知错误
    UNKNOWN(1001, "未知错误"),
    REQUEST_ERROR(1002, "请求错误"),
    SYSTEM_ERROR(1003, "系统错误"),
     private static Map<Integer, RestErrorEnum> map;
    private Integer code;
    private String description;

    public static RestErrorEnum codeOf(int code) {
        if (map == null) {
            map = Arrays.stream(RestErrorEnum.values()).collect(Collectors.toMap(RestErrorEnum::getCode, e -> e));
        }
        return map.getOrDefault(code, UNKNOWN);
    }

    public static RestErrorEnum descriptionOf(String description) {
        for (RestErrorEnum errorEnum : RestErrorEnum.values()) {
            if (errorEnum.getDescription().equals(description)) {
                return errorEnum;
            }
        }
        return null;
    }

    RestErrorEnum(Integer code, String description) {
        this.code = code;
        this.description = description;
    }

    public Integer getCode() {
        return code;
    }

    public String getDescription() {
        return description;
    }
}

1.2这里是自定义的实现,如果想更进一步的了解可以点到注解的源码里看一下.完成下面的配置就可以了

@ControllerAdvice
@Slf4j
public class ResponseBodyAdviceEx implements ResponseBodyAdvice<Object> {

    @ExceptionHandler(value = RuntimeException.class)
    @ResponseBody
    public RestResponse<String> errorHandler(Throwable t) {
        RestException e;
        if (t instanceof RestException) {
            e = ((RestException) t);
        } else {
            t.printStackTrace();
            if (t.getCause() != null) {
                //减少暴露给用户的异常信息
                e = new RestException(RestErrorEnum.UNKNOWN, t.getCause().getMessage());
            } else {
                e = new RestException(RestErrorEnum.UNKNOWN, t.getMessage());
            }
        }
        return new RestResponse<>(e.getCode(), e.getResult(), e.getMessage());
    }
     @Override
    public boolean supports(MethodParameter returnType,
                            Class<? extends HttpMessageConverter<?>> converterType) {//TODO方法实现}
    
    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {//方法实现}                        
}

1.3我们可以使用下面的代码对异常信息抛出给前端.

throw new RestException(RestErrorEnum.PPM_WORKTIME_IS_WORK_CLASS_NAME);

2.统一接口响应
我们上面介绍了ResponseBodyAdvice这个接口的两个方法和基本原理下面看代码!!!
2.1定义一个注解(使用统一配置信息响应就加上的注解)

/**
 * 仅在ResponseBodyAdvice 指定的注解下有效。
 */
@Target({TYPE, METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBodyWrapper {
    boolean value() default true;
}

2.2定义统一返回的响应类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class RestResponse<T> {
    private Integer code;
    private T result;
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private String description;
}

2.3自定义方法内的实现

@ControllerAdvice
@Slf4j
public class ResponseBodyAdviceEx implements ResponseBodyAdvice<Object> {

    @ExceptionHandler(value = RuntimeException.class)
    @ResponseBody
    public RestResponse<String> errorHandler(Throwable t) {
        RestException e;
        if (t instanceof RestException) {
            e = ((RestException) t);
        } else {
            t.printStackTrace();
            if (t.getCause() != null) {
                //减少暴露给用户的异常信息
                e = new RestException(RestErrorEnum.UNKNOWN, t.getCause().getMessage());
            } else {
                e = new RestException(RestErrorEnum.UNKNOWN, t.getMessage());
            }
        }
        return new RestResponse<>(e.getCode(), e.getResult(), e.getMessage());
    }

    @Override
    public boolean supports(MethodParameter returnType,
                            Class<? extends HttpMessageConverter<?>> converterType) {
        return "org.springframework.http.converter.json.MappingJackson2HttpMessageConverter".equals(converterType.getName());
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        //response.getHeaders().add("x-frame-options","SAMEORIGIN");
        if (body instanceof RestResponse) {
            // 对于请求异常情况,设置响应码为500
            //response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            return body;
        }
        if (needToWrapper(returnType)) {
            return new RestResponse<>(RestErrorEnum.SUCCESS.getCode(), body, RestErrorEnum.SUCCESS.getDescription());
        }
        return body;
    }

    private boolean needToWrapper(MethodParameter returnType) {
        // 类含有ResponseBodyWrapper并且value为true
        ResponseBodyWrapper type = returnType.getContainingClass().getAnnotation(ResponseBodyWrapper.class);
        if (type != null && type.value()) {
            return true;
        }
        // 方法含有ResponseBodyWrapper并且value为true
        ResponseBodyWrapper method = returnType.getMethodAnnotation(ResponseBodyWrapper.class);
        return method != null && method.value();
    }
}

2.4使用时在类或方法上加入自定义的注解即可

@Api(tags = "工时类别")
@RestController
@RequestMapping("worktime/workingclass")
@ResponseBodyWrapper
public class WorkingclassController {}

这里解释一下supports()指定一个HttpmessageConverter的返回类可以参考如下图片在这里插入图片描述

感谢各位读者的阅读,如果持有不同观点或者文章出现错误问题欢迎各位读者评论与私信.如果觉得小编的文章对您有帮助就给小编点个赞吧!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值