【CV即用】 理解和使用Spring Boot中的异常处理和响应体增强

Spring Boot提供了一种高效、优雅的方式来处理REST API的返回数据和异常情况,确保API的响应数据结构统一,并且能够优雅的处理服务端异常。以下是如何在Spring Boot应用中实现响应体的统一包装和全局异常处理。

【CV即用】 使用Axios拦截器实现前端HTTP请求的统一处理

统一响应体结构

为了确保API的响应格式具有一致性,我们可以创建一个ApiResponse类作为API响应的标准格式包装,这个类包含三个属性:code, message, 和 data,代表了状态码、返回信息和数据负载。

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse<T> {
    private int code;
    private String message;
    private T data;
}

控制器增强:响应体包装

在Spring中使用@ControllerAdviceResponseBodyAdvice接口,可以对响应体进行预处理。以下是一个ApiResponseAdvice类的示例,该类确保所有的响应体都将会被包装在ApiResponse对象中。

import cn.hutool.json.JSONUtil;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.annotation.Resource;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

@ControllerAdvice
public class ApiResponseAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 检查方法的返回类型,跳过 ResponseEntity<Resource> 类型或其他特定类型的处理
        if (ResponseEntity.class.isAssignableFrom(returnType.getParameterType())) {
            Type type = returnType.getGenericParameterType();
            if (type instanceof ParameterizedType) {
                Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
                for (Type argument : typeArguments) {
                    if (argument instanceof Class && Resource.class.isAssignableFrom((Class<?>) argument)) {
                        return false; // 对于文件下载等不做处理
                    }
                    // 添加其他逻辑以正确处理 WildcardType 或其他复杂类型
                }
            }
        }
        // 在此处添加其他条件检查,以决定是否应用这个ResponseBodyAdvice

        // 默认情况下适用这个ResponseBodyAdvice
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        // 设置字符编码为UTF-8
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        // 如果body已经是ApiResponse,则无需处理
        if (body instanceof ApiResponse) {
            return body;
        }

        // 对于String特殊处理,因为Spring默认StringHttpMessageConverter将String转换成HTTP响应体时
        // 无法正确处理非String类型(如果直接包装成ApiResponse的话)
        if (body instanceof String) {
            // 把处理后的ApiResponse对象转换成JSON字符串,因为直接返回ApiResponse对象会和String的预期类型冲突。
            ApiResponse<Object> res = new ApiResponse<>(HttpStatus.OK.value(), "成功", body);
            return JSONUtil.toJsonStr(JSONUtil.parse(res));
        }

        // 其他情况,把body包装在ApiResponse里面返回
        return new ApiResponse<>(HttpStatus.OK.value(), "成功", body);
    }

}

控制器示例:API接口

我们创建了一个TestController类,其中包含了几种HTTP方法示例。一个是通过@GetMapping处理GET请求的示例;另一个是用@PostMapping处理POST请求的示例;第三个是一个将引发异常的GET方法。

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/get")
    public String test() {
        return "成功";
    }

    @PostMapping("/post")
    public String post(@RequestBody String body) {
        log.info("接收===>{}", body);
        return body;
    }

    @GetMapping("/exception")
    public String exception() {
        int i = 1 / 0;
        return "";
    }

}

自定义异常类

在开发复杂的应用时,我们可能需要定义自定义异常类以更好地描述特定的错误情况。自定义异常可以帮助我们在出现特定业务规则违反或异常情况时,提供更加详细和具体的错误信息。

在示例代码中,我们定义了一个名为CustomException的自定义异常类,它继承自RuntimeException

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class CustomException extends RuntimeException {
    public CustomException(String message) {
        super(message);
    }
}

通过@ResponseStatus注解,我们指定了当CustomException被抛出时,默认的HTTP状态码是400 Bad Request。这意味着如果在控制器方法中抛出这个异常,Spring框架会捕获它,并自动返回状态码为400的响应给客户端。

自定义异常通常用于业务逻辑错误或应用的某些特定状态下,相比较于Java标准库中的异常,自定义异常提供了更好的可读性和错误追踪能力。在全局异常处理器中捕获到这个自定义异常时,我们可以返回一个包含错误详情的ApiResponse,这使得API的消费者能够轻松地理解和处理错误。

使用自定义异常的好处包括:

提高代码的可读性和维护性
实现错误处理的一致性
方便和前端进行协议上的约定,提高前后端分离开发的效率
请注意,总是建议在抛出自定义异常前进行仔细考虑,确保像这样的自定义异常是为了处理特定的业务场景,而不是简单地替换标准异常。

全局异常处理

除了响应体增强,我们还需要一个全局异常处理机制来捕获和包装异常。我们可以通过@ControllerAdvice注解创建一个全局异常处理类GlobalExceptionHandler

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理所有未知的异常
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> handleGlobalException(Exception ex, WebRequest request) {
        ApiResponse<String> response = new ApiResponse<>();
        response.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
        response.setMessage(ex.getMessage());
        response.setData(null);
        return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    /**
     * 处理自定义异常
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(CustomException.class)
    public ResponseEntity<ApiResponse<String>> handleCustomException(CustomException ex, WebRequest request) {
        ApiResponse<String> response = new ApiResponse<>();
        response.setCode(HttpStatus.BAD_REQUEST.value());
        response.setMessage(ex.getMessage());
        response.setData(null);
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }

}

这个类包含了处理所有异常handleGlobalException的方法,当程序抛出异常时,方法会自动捕获,并返回一个格式良好的ApiResponse对象。还有一个专门处理自定义异常handleCustomException的方法。

这两种机制结合在一起,无论是正常的响应还是异常,都可以确保用户接收到统一格式的JSON响应。通过这种方式,我们能提高API的可用性和客户端开发人员的效率。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值