超實用的SpringBoot中全局異常處理器(供參考)

import com.xxx.exception.CodingCloudException;
import com.xxx.exception.ResourceNotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.security.auth.login.FailedLoginException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolationException;
import java.io.BufferedReader;
import java.io.StringReader;
import java.util.List;
import java.util.Objects;

import static com.xxx.utils.Constants.BEARER;
import static org.springframework.http.HttpHeaders.WWW_AUTHENTICATE;

/**
 * 全局異常處理類
 *
 */
@Slf4j
@ControllerAdvice
@RestController
public class GlobalExceptionHandler implements ErrorController {

    private static final String PATH = "/error";
    private static final String JSON_ERROR_INFO = "JSON parse error:";
    private static final String SEMICOLON = ";";
    private static final char BLANK = ' ';
    private static final int CUT_LENGTH = 100;

    @RequestMapping(value = PATH, produces = {MediaType.APPLICATION_JSON_VALUE})
    @ResponseBody
    public ResponseEntity<Body<Void>> errorHandler(HttpServletResponse response, HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        String message = String.valueOf(request.getAttribute("javax.servlet.error.message"));
        log.warn("[Handle_Error] status:{}, message:{}", statusCode, message);
        response.setStatus(statusCode);
        return ResponseUtil.of(HttpStatus.valueOf(statusCode), message);
    }

    @Override
    public String getErrorPath() {
        return PATH;
    }

    @ExceptionHandler
    @ResponseBody
    public ResponseEntity<Body<Void>> exceptionHandler(MissingServletRequestParameterException e) {
        StringBuilder sb = new StringBuilder().append("未提供必選請求參數 '")
                .append(e.getParameterName()).append("'(").append(e.getParameterType()).append(")");
        log.warn("[Handle_MissingServletRequestParameterException] {}", sb.toString());
        return ResponseUtil.badRequest(sb.toString());
    }

    @ExceptionHandler
    @ResponseBody
    public ResponseEntity<Body<Void>> exceptionHandler(IllegalArgumentException e) {
        log.warn("[Handle_IllegalArgumentException]", e);
        return ResponseUtil.badRequest("參數無效:" + e.getMessage());
    }

    @ExceptionHandler
    @ResponseBody
    public ResponseEntity<Body<Void>> exceptionHandler(MethodArgumentTypeMismatchException e) {
        StringBuilder sb = new StringBuilder().append("參數 '").append(e.getName())
                .append("'(").append(e.getRequiredType().getSimpleName()).append(")類型不匹配: ").append(e.getValue());
        log.warn("[Handle_MethodArgumentTypeMismatchException] {}", sb.toString());
        return ResponseUtil.badRequest(sb.toString());
    }

    @ExceptionHandler
    @ResponseBody
    public ResponseEntity<Body<Void>> exceptionHandler(MethodArgumentNotValidException e) {
        String message = getErrorMsg(e.getBindingResult());
        log.warn("[Handle_MethodArgumentNotValidException] {}", message);
        return ResponseUtil.badRequest("參數無效:" + message);
    }

    @ExceptionHandler
    @ResponseBody
    public ResponseEntity<Body<Void>> exceptionHandler(BindException e) {
        String message = getErrorMsg(e.getBindingResult());
        log.warn("[Handle_BindException] {}", message);
        return ResponseUtil.badRequest(message);
    }

    private String getErrorMsg(BindingResult bindingResult) {
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        String message = "";
        if (!fieldErrors.isEmpty()) {
            FieldError error = fieldErrors.get(0);
            message = error.getField() + " " + error.getDefaultMessage();
        }
        return message;
    }

    @ExceptionHandler
    @ResponseBody
    public ResponseEntity<Body<Void>> exceptionHandler(ConstraintViolationException e) {
        // 如有需求,可以自定義輸出消息格式和內容。
        log.warn("[Handle_ConstraintViolationException]", e);
        return ResponseUtil.badRequest("約束違規:" + e.getMessage());
    }

    @ExceptionHandler
    @ResponseBody
    public ResponseEntity<Body<Void>> exceptionHandler(ResourceNotFoundException e) {
        log.warn("[Handle_ResourceNotFoundException]", e);
        return ResponseUtil.serverError("資源丟失:" + e.getMessage());
    }

    @ExceptionHandler
    @ResponseBody
    public ResponseEntity<Body<Void>> exceptionHandler(HttpMessageNotReadableException e) {
        log.error("[Handle_HttpMessageNotReadableException]", e);
        String jsonErrorMsg;
        if (e.getLocalizedMessage().contains(JSON_ERROR_INFO) &&
                Objects.nonNull(jsonErrorMsg =
                        dealWithJsonExceptionError(e.getLocalizedMessage()))) {
            return ResponseUtil.badRequest("格式轉換錯誤,請檢查" + jsonErrorMsg + "字段");

        }
        return ResponseUtil.serverError("消息不可讀:" + StringUtils.substring(e.getMessage(), 0, CUT_LENGTH));
    }

    /**
     * 針對性處理JsonException,友好返回異常信息
     *
     * @param errorMsg
     * @return
     */
    private String dealWithJsonExceptionError(String errorMsg) {
        int lastSemicolon = errorMsg.lastIndexOf(SEMICOLON);
        if (lastSemicolon != -1) {
            int startIndex = lastSemicolon - 1;
            while (startIndex > 0 && errorMsg.charAt(startIndex) != BLANK) {
                startIndex--;
            }
            return errorMsg.substring(++startIndex, lastSemicolon);
        }

        return null;
    }

    @ExceptionHandler
    @ResponseBody
    public ResponseEntity<Body<Void>> exceptionHandler(
            HttpServletRequest request, FailedLoginException e) {
        String msg = e.getMessage();
        log.warn("[Handle_FailedLoginException] {}", msg);
        return ResponseUtil.badRequest("登錄失敗:" + msg);
    }

    @ExceptionHandler
    @ResponseBody
    public ResponseEntity<Body<Void>> exceptionHandler(HttpServletResponse response, AccessDeniedException e) {
        log.warn("[Handle_AccessDeniedException] {}", e.getMessage());
        StringBuilder wwwAuthenticateBuilder = new StringBuilder();
        wwwAuthenticateBuilder.append(BEARER).append(" ")
                .append("realm").append("=").append("xiaohoucode.com").append(", ")
                .append("error").append("=").append("insufficient_scope");
        response.setHeader(WWW_AUTHENTICATE, wwwAuthenticateBuilder.toString());
        return ResponseUtil.forbidden("拒絕訪問:" + e.getMessage());
    }


    @ExceptionHandler
    @ResponseBody
    public ResponseEntity<Body<Void>> exceptionHandler(CodingCloudException e) {
        log.error("[Handle_CodingCloudException]", e);
        return ResponseUtil.serverError("系統異常:" + StringUtils.substring(e.getMessage(), 0, CUT_LENGTH));
    }

    @ExceptionHandler
    @ResponseBody
    public ResponseEntity<Body<Void>> exceptionHandler(Exception e) {
        log.error("[Handle_Exception]", e);
        return ResponseUtil.serverError("抱歉,系統發生意外了。["
                + StringUtils.substring(e.getMessage(), 0, CUT_LENGTH) + "]");
    }

    private String getSimpleStackTrace(Exception e) {
        StringBuilder simpleStackTraceBuilder = new StringBuilder();
        try (BufferedReader bufferedReader = new BufferedReader(new StringReader(
                ExceptionUtils.getStackTrace(e)))) {
            String line = bufferedReader.readLine();
            simpleStackTraceBuilder.append(line).append("\n");
            while ((line = bufferedReader.readLine()) != null) {
                if (StringUtils.contains(line, "com.tal.")) {
                    simpleStackTraceBuilder.append(line).append("\n");
                }
            }
        } catch (Exception ex) {
            // do nothing
        }
        return simpleStackTraceBuilder.toString();
    }
}

 

發佈了6 篇原創文章 · 獲贊 3 · 訪問量 2293
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章