后台框架-统一异常管理

搭建后台框架全局异常管理是一个很重要的部分,好在SpringBoot提供了很好的处理方法

使用@ControllerAdvice

@ControllerAdvice是Spring MVC中的一个全局异常处理注解,它允许在一个地方集中处理所有控制器抛出的异常。通过使用@ControllerAdvice,可以避免在每个控制器中重复编写异常处理逻辑,从而使代码更加简洁和易于维护。

基本用法

要使用@ControllerAdvice,创建一个类,并在该类上添加@ControllerAdvice注解。然后,在该类中定义多个@ExceptionHandler方法,每个方法处理一种特定的异常类型。

示例代码

package org.example.web.web;

import org.example.web.model.R;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理全局异常
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Object exceptionHandler(Exception e){
        // 运行时异常
        if(e instanceof RuntimeException){
            RuntimeException ex = (RuntimeException) e;
            return R.error(500, ex.getMessage());
        }
        return R.error(999, e.getMessage());
    }
}

继承BasicErrorController

BasicErrorController是Spring Boot中用于处理错误页面的默认控制器。当应用程序发生错误时,Spring Boot会自动调用BasicErrorController来处理错误,并返回相应的错误页面。

基本用法

BasicErrorController处理两个主要点:
/error:处理所有类型的错误。
/error/{code}:处理特定HTTP状态码的错误。
默认情况下,BasicErrorController会返回一个简单的HTML错误页面,显示错误的状态码和消息。你可以通过自定义BasicErrorController来改变这种行为。

示例代码

package org.example.web.web;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.example.web.model.R;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;

@Slf4j
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class CustomErrorController extends BasicErrorController {

    @Value("${server.error.path:${error.path:/error}}")
    private String path;

    public CustomErrorController(ServerProperties serverProperties) {
        super(new DefaultErrorAttributes(), serverProperties.getError());
    }

    /**
     * 覆盖默认的JSON响应
     */
    @Override
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        HttpStatus status = getStatus(request);
        Map<String, Object> map = new HashMap<>(16);
        Map<String, Object> originalMsgMap = getErrorAttributes(request, ErrorAttributeOptions.defaults());
        String path = (String) originalMsgMap.get("path");
        String error = (String) originalMsgMap.get("error");
        String message = (String) originalMsgMap.get("message");
        StringJoiner joiner = new StringJoiner(",", "[", "]");
        joiner.add(path).add(error).add(message);
        map.put("code", status.value());
        map.put("message", joiner.toString());
        return new ResponseEntity<Map<String, Object>>(map, status);
    }

    /**
     * 覆盖默认的HTML响应
     */
    @Override
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        // 请求的状态
        HttpStatus status = getStatus(request);
        response.setStatus(getStatus(request).value());
        Map<String, Object> model = getErrorAttributes(request, ErrorAttributeOptions.defaults());
        ModelAndView modelAndView = resolveErrorView(request, response, status, model);
        // 指定自定义的视图
        log.error("{} - {} - {}", request.getRequestURI(), status.value(), JSON.toJSONString(model));
        if (status.value() == 404) {
            return (modelAndView == null ? new ModelAndView("err/404", model) : modelAndView);
        } else if (status.value() == 500) {
            return (modelAndView == null ? new ModelAndView("err/500", model) : modelAndView);
        } else {
            return (modelAndView == null ? new ModelAndView("err/other", model) : modelAndView);
        }
    }
}

静态错误页面

/src/main/resources/templates/err/404.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="zh">
<head>
    <title th:text="${'ERROR - ' + status}"></title>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width:device-width,initial-scale=1" />
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <link th:href="@{/css/style.css}" rel="stylesheet" />
</head>
<body>
    ERROR-<span th:text="${status}"></span>-<span th:text="${error}"></span>
</body>
</html>

/src/main/resources/templates/err/500.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="zh">
<head>
    <title th:text="${'ERROR - ' + status}"></title>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width:device-width,initial-scale=1" />
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <link th:href="@{/css/style.css}" rel="stylesheet" />
</head>
<body>
    ERROR-<span th:text="${status}"></span>-<span th:text="${error}"></span>
</body>
</html>

ResultResponseAdvice

package org.example.web.web;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.web.model.R;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
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.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.Map;

/**
 * 对controller 层中 ResponseBody 注解方法,进行增强拦截
 */
@ControllerAdvice
public class ResultResponseAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 返回true表示对所有Controller的返回值进行处理
        return true;
    }

    /**
     * 如果开启,就会对返回结果进行处理
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        // 设置响应类型为json
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
        if (body instanceof R) {
            // 如果body返回的是ResultMsg类型的对象,不进行增强处理
            response.setStatusCode(HttpStatus.valueOf(((R<?>) body).getCode()));
            return body;
        }
        if (body instanceof String) {
            // 如果body返回的是String类型的对象,单独处理
            return toJson(body);
        }
        if (body instanceof Map) {
            Map<String, Object> map = (Map<String, Object>) body;
            if (map.containsKey("code") && map.containsKey("message")) {
                int code = Integer.parseInt(map.get("code").toString());
                response.setStatusCode(HttpStatus.valueOf(code));
                if (code == 200) {
                    return R.ok(map.get("message").toString());
                } else {
                    return R.error((int)map.get("code"), map.get("message").toString());
                }
            } else {
                return R.ok(map);
            }
        }
        return R.ok(body);
    }

    private Object toJson(Object body) {
        try {
            return new ObjectMapper().writeValueAsString(R.ok(body));
        } catch (JsonProcessingException e) {
            throw new RuntimeException("无法转发json格式", e);
        }
    }
}

最后效果

访问一个不存在的页面时:
在这里插入图片描述
访问一个不存在的接口时:
在这里插入图片描述
访问一个抛错误的接口时:

    @RequestMapping("/test")
    @ResponseBody
    public User test() {
        throw new RuntimeException("User not found");
    }

在这里插入图片描述

源代码

访问后台框架-统一异常处理源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

angushine

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值