SpringBoot ​@ControllerAdvice 处理异常

应用中的异常,有两件事要考虑,怎么处理这个异常,怎么把异常可读性高地返回给前端用户

1.怎么把异常可读性高的返回给前端用户或API的消费者

自定义错误代码和错误内容

2.怎么处理异常

比如遇到某个异常时需要发邮件通知IT团队

@ControllerAdvice 是 Spring Framework 提供的一个注解,用于定义全局的异常处理、数据绑定和模型属性的增强。

它的主要作用是集中管理控制器的全局逻辑,例如异常处理,而不需要在每个控制器中重复编写相同的代码。  

主要功能

全局异常处理:通过 @ExceptionHandler 注解 捕获并处理控制器中抛出的异常。

全局数据绑定:通过 @InitBinder 注解对请求参数进行预处理。

全局模型属性:通过 @ModelAttribute 注解为所有控制器提供公共的模型数据

3.自定义错误代码和内容,并且使用@ControllerAdvice统一处理异常

3.1 自定义错误码,可以把Demo换成projectname

public enum ExceptionCode {
    IN_VALID_REQUEST("Demo0001", ""),
    BAD_REQUEST("Demo400", "Bad Request"),
    NOT_FOUND("Demo404", "Resource Not Found"),
    INTERNAL_SERVER_ERROR("Demo500", "Internal Server Error");

    private final String code;
    private final String message;

    ExceptionCode(String code, String message) {
        this.code = code;
        this.message = message;
    }

    public String getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

3.2 自定义异常InvalidationException

public class InvalidationRequestException extends RuntimeException {

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

    public InvalidationRequestException(String message, Throwable cause) {
        super(message, cause);
    }
}

3.3 自定义ExceptionAdvice,统一处理exception

当controller中抛出InvalidationRequestException时,就会被@ControllerAdvice 中的 @ExceptionHandler handleInvalidationException()方法捕获并处理该异常

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(InvalidationRequestException.class)
    @ResponseBody
    public ResponseEntity<Map<String, String>> handleInvalidationException(InvalidationRequestException ex) {
        logger.warn("InvalidationRequestException: {}", ex.getMessage());

        //其它异常处理逻辑,比如发邮件,打电话通知IT团队
        
        //返回给UI或API消费者
        Map<String, String> response = new HashMap<>();
        response.put("code", ExceptionCode.IN_VALIDATED_REQUEST.getCode());
        response.put("detail",ex.getMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);

    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGenericException(Exception ex) {
        return new ResponseEntity<>("An error occurred: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

3.4 controller, 抛出InvalidationException异常

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @GetMapping("/validate")
    public String validateParameter(@RequestParam String input) {
        if (input == null || input.trim().isEmpty()) {
            throw new InvalidationRequestException("Input parameter is invalid or empty");
        }
        return "Input is valid: " + input;
    }
}

3.5 测试

启动应用,访问 http://localhost:8082/validate?input

校验失败时,返回http状态码是400,body如下

{

  "code": "Demo0001",

  "detail": "Input parameter is invalid or empty"

}

==========================================

1.异常直接抛出

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @GetMapping("/throwException")
    public String throwException() {
        throw new RuntimeException("This is a runtime exception!");
    }
}

启动应用,访问 http://localhost:8082/throwException

会获得一个状态码为500的异常 An error occurred: This is a runtime exception!

思考:这个异常对用户或者call API的消费者来说,可读性太低,不能明确的知道是什么错误

2.自定义状态码和错误信息,返回给前端用户或者API 消费者可读性高的异常

自定义错误码,可以把Demo换成projectname

public enum ExceptionCode {
    IN_VALIDATED_REQUEST("Demo0001", ""),
    BAD_REQUEST("Demo400", "Bad Request"),
    NOT_FOUND("Demo404", "Resource Not Found"),
    INTERNAL_SERVER_ERROR("Demo500", "Internal Server Error");

    private final String code;
    private final String message;

    ExceptionCode(String code, String message) {
        this.code = code;
        this.message = message;
    }

    public String getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

controller抛出异常

import com.example.demo_async.exception.ExceptionCode;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class DemoController {

    @GetMapping("/testErrorCode")
    public ResponseEntity<Map<String, String>> testErrorCode() {
        Map<String, String> response = new HashMap<>();
        response.put("code", ExceptionCode.BAD_REQUEST.getCode());
        response.put("detail", ExceptionCode.BAD_REQUEST.getMessage());
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }
}

返回http status  400, body 如下

{

  "code": "Demo400",

  "detail": "Bad Request"

}

思考:如果有很多个controller方法都会抛出这个异常,就需要在每个方法里写一遍异常处理的逻辑,会产生大量的重复代码,怎么解决呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值