1. 程式人生 > >Spring MVC 全域性異常處理-RESTAPI介面返回統一JSON格式-自定義異常處理--404異常捕捉

Spring MVC 全域性異常處理-RESTAPI介面返回統一JSON格式-自定義異常處理--404異常捕捉

寫之前大概兩週草草的將一些程式碼儲存在草稿箱,今天有空來看,結果都沒有了【怨念】—重新整理一下了 —–【轉載請標註出處

  • 第一部分:需求
  • 第二部分:實現方式
  • 第三部分:404異常捕捉不能實現分析
  • 第四部分:原因和原始碼分析
  • 第五部分:最終總結

需求

  • 本意是想針對對外REST介面的返回格式進行統一,結果404錯誤始終無法被捕捉

實現方式

對於目前的主流方式全部嘗試了一遍,主要有三種,還有其他一些異類採用Filter之類的方式實現,如需可以自取

1. SimpleMappingExceptionResolver
採用xml配置方式,程式碼零入侵 。自由性不足,獲取異常資訊少(只有異常資訊)。


2. HandlerExceptionResolver
自定義類實現該類。可以實現統一異常處理裡。我在實現的時候卻沒有生效,而是走了其自定義的DefaultHandlerExceptionResolver,具體原因下面分析
3. @ExceptionHandler註解
這種捕捉方式要求和被捕捉Controller在同一個類中,一般實現方式是把ExceptionHandler放在BaseController中繼承,自由性差。
[email protected]註解
這種需要配合@ExceptionHandler屬於第三種方式變種,我使用的則是這種方式的變種,下面詳解。

原因和原始碼分析

首先來分析異常的處理過程

感謝網上某位同學

上面是簡化的請求流轉圖,感謝某位同學的圖片。

下面是程式設計師趙鑫的原理分析圖,我搬來一用。當然我沒有授權,如果有爭議,我援引網際網路資訊共享條例自護(黑人問號臉)
感謝程式設計師趙鑫同學

照例感謝程式設計師趙鑫同學,想看再詳細的分析請轉貼這裡

上圖是SpringMVC的異常處理器結構,HandlerExceptionResolver是一個介面,留給自定義的時候使用。

異常處理器的處理順序是:異常->HandlerExceptionResolver->自定義異常->預設異常處理器

SpringMVC的異常處理器通過實現Orderd介面,定義Order數值為每個異常處理器排序,圖中可以看到預設異常處理器都繼承於AbstractHandlerExceptionResolver。看圖:

這裡寫圖片描述

預設異常處理器都實現了doResolverException()方法。

圖上的每一個處理器其實都代表了可以實現全域性自定義處理的一種或者多種方式。

404異常捕捉不能實現分析

在我實現全域性處理異常的時候,發現404異常並沒有被捕捉,參考多方資料後發現,SpringleMVC的機制預設是對404異常不進行丟擲動作的,直接在Response中設定錯誤程式碼,直接返回。具體看圖:

這裡寫圖片描述

圖中的屬性 throwExceptionIfNoHandlerFound = false 就是404異常丟擲錯誤與否的判斷屬性,下面是判斷原始碼,throwExceptionIfNoHandlerFound為true則會丟擲異常

這裡寫圖片描述

原因和原始碼分析

下面對幾種實現方式進行解析

  • 1.SimpleMappingExceptionResolver
    程式碼如下:這裡不做詳解,缺點是不靈活,獲取資訊引數有限
  <!-- 出現異常會跳到這個頁面,總錯誤處理-->
 <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
      <property name="defaultErrorView">
           <value>/error/error</value>
      </property>
      <property name="defaultStatusCode">
           <value>500</value>
      </property>
      <property name="warnLogCategory">
           <value>org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver</value>
      </property>
  <!-- 如果不設定exceptionMappings,就會全域性異常捕獲 -->
      <property name="exceptionMappings">
           <props>
                <prop key="Java.sql.SQLException">/error/error</prop>
                <prop key="Java.lang.RuntimeException">/error/error</prop>
                <prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">/error/error</prop>
           </props>
      </property>
</bean>
  • 2.HandlerExceptionResolver
    這種實現方式會受到容器載入順序影響(可能是這個原因),如果沒有載入完全,會按照SpringMVC預設的配置檔案中的處理順序進行處理
    這裡寫圖片描述
@Component
public class MyExceptionHandler implements HandlerExceptionResolver {
     public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
          Map<String, Object> model = new HashMap<String, Object>();
          model.put("ex", ex);

          // 根據不同錯誤轉向不同頁面
          if(ex instanceof BusinessException) {
               return new ModelAndView("error-business", model);
          }else if(ex instanceof ParameterException) {
               return new ModelAndView("error-parameter", model);
          } else {
               return new ModelAndView("error", model);
          }
     }
}
這種需要在Spring的配置檔案applicationContext.xml中增加以下內容:
//有一種說法,如果不起作用,id寫成這樣說不定能解決問題,因為載入的時候是按照這個id去載入的
< bean id="handlerExceptionResolver" class="cn.basttg.core.exception.MyExceptionHandler"/>  
  • 3.ControllerAdvice(和ExceptionHandler放在一起了)

@ControllerAdvice  
public class ExceptionHandler {  

    @ResponseBody  
    @org.springframework.web.bind.annotation.ExceptionHandler(Exception.class//這裡可以選擇其他具體的異常)  
    public ResponseModel handleUnexpectedServerError(Exception ex) {  
        ex.printStackTrace();  

        // 處理異常  
        ResponseModel response = new ResponseModel();  

        String errorMsg = ex.getMessage();  
        response.setCode(Integer.valueOf(ResultCode.SYSTEM_EXCEPTION));  
        if(errorMsg.contains("excpStart")){  
            errorMsg = errorMsg.substring(errorMsg.indexOf("excpStart") + 9, errorMsg.indexOf("excpEnd"));  
        }  
        response.setMessage(errorMsg);  

        // 返回資料  
        return response;  
    }  
  • 4.ControllerAdvice(我的實現方式)
@ControllerAdvice
public class GlobalExceptionResolver extends DefaultHandlerExceptionResolver {

    @ExceptionHandler(value = Exception.class)
    public ModelAndView defaultErrorHandler(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        String url = request.getServletPath();
        if (url.startsWith("/api")) {//api返回異常攔截

            if (ex instanceof HttpRequestMethodNotSupportedException) {
                setResponseParam(response, 405, "請求方式錯誤!");
                return null;
            }

            if (ex instanceof MissingServletRequestParameterException) {
                setResponseParam(response, 400, "錯誤請求!");
                return null;
            }

            if (ex instanceof NoHandlerFoundException) {
                //可以進行其他方法處理,LOG或者什麼詳細記錄,我這裡直接返回JSON
                setResponseParam(response, 404, "請求路徑錯誤!");
                return null;
            }

            setResponseParam(response, 500, "伺服器內部錯誤!服務暫時不可用!");
            return null;
        }

        //這裡呼叫父類的異常處理方法,實現其他不需要的異常交給SpringMVC處理
        return super.doResolveException(request, response, handler, ex);

    }

    private void setResponseParam(HttpServletResponse response, int code, String msg) throws IOException {
        JSONObject j = JSONObject.fromObject(R.error(code, msg));
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
        response.getWriter().print(j.toString());
    }
}

參考:

最終總結

寫了近3小時,一邊回憶一邊寫,好累,下次有好的再說吧,需要支援點這裡

相關推薦

Spring MVC 全域性異常處理-RESTAPI介面返回統一JSON格式-定義異常處理--404異常捕捉

寫之前大概兩週草草的將一些程式碼儲存在草稿箱,今天有空來看,結果都沒有了【怨念】—重新整理一下了 —–【轉載請標註出處】 第一部分:需求 第二部分:實現方式 第三部分:404異常捕捉不能實現分析 第四部分:原因和原始碼分析 第五部分:最終總結 需求

Restful介面返回統一資料格式

一、定義返回的資料格式: public class ResponseMessage<T> {     private int code;     private boolean state;     private T data;     private St

Spring MVC全域性異常返回JSON異常資料

問題:         當前專案是作為手機APP後臺支援,使用spring mvc + mybaits + shiro進行開發。後臺服務與手機端互動是傳送JSON資料。如果後臺發生異常,會直接返回異常頁面,顯示異常內容,如果是404請求不到資源或者500這類伺服器的問題,可能會導致返回404和500異常

Spring MVC溫故而知新 – 參數綁定、轉發與重定向、異常處理、攔截器

單獨 UC exclude require 加載 pre buffered nts 節點 請求參數綁定 當用戶發送請求時,根據Spring MVC的請求處理流程,前端控制器會請求處理器映射器返回一個處理器,然後請求處理器適配器之心相應的處理器,此時處理器映射器會調用Spr

Spring MVC 4.1.4 RESTFUL風格返回JSON資料406錯誤處理 .

今天在使用spring4.1.4,使用ResponseBody註解返回JSON格式的資料的時候遇到406錯誤。 解決辦法,匯入jackson2.X的jar包: jackson-annotations-2.4.4.jar、jackson-core-2.4.4.jar、jack

Spring MVC 介面返回json資料過濾空值

前後端互動時,後端返回給前端是一個json,json中的值是由一個物件轉換而來的,有時候該物件中可能某些欄位的值是空,返回給前端的json就會出現某些key的value是空,在默寫情況下不利於前端處理。 其實在後端返回時可以進行資料過濾,將物件是為空的欄位自動過濾掉。一行程

SpringMVC下全域性異常統一響應返回JSON定義異常

Spring MVC框架下一般會自定義全域性異常,再通過Spring MVC下的全域性異常攔截全部轉化為自定義異常。但在controller層處理異常有很多的try{}catch{},影響效能。所以為了能夠處理異常的同時返回相同的資料結構,定義一個全域性異常統一響應以返回JS

spring mvc 避免IE執行AJAX時,返回JSON出現下載檔案

<!-- 避免IE執行AJAX時,返回JSON出現下載檔案 --> <bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.M

spring mvc 在同一個controller 中同時返回多種格式的資料 (xml json atom)

 在本篇文章中,我們來學習spring MVC中返回的資料格式,這種方式在寫介面時,非常適用也非常實用, 其實在spring mvc 中本身對資料的返回格式已經封裝的非常好,同時支援多種格式,

Spring MVC中Controller如何將資料返回給頁面

要實現Controller返回資料給頁面,Spring MVC 提供了以下幾種途徑: ModelAndView:將檢視和資料封裝成ModelAndView物件,作為方法的返回值,資料最終會存到HttpServletRequest物件中! Model物件:通過給方法新增引用

spring mvc 集合、陣列傳參(本質是json物件的處理)

問題概述: 在前臺,js已經獲取到對應的資料,但這些資料是集合型別(也就是json陣列格式的),此時無法傳遞到後臺。 要進行處理,將json格式的資料轉換成String型別的,就可以成功獲取到了。 下面貼上程式以供參考: 1.實體類 public class Perso

Spring MVC學習------------核心類與介面

核心類與介面: 先來了解一下,幾個重要的介面與類。現在不知道他們是幹什麼的沒關係,先混個臉熟,為以後認識他們打個基礎。 DispatcherServlet   -- 前置控制器 HandlerMapping介面 -- 處理請求的對映 HandlerMap

spring mvc通過ModelAndView跳轉介面CSS js樣式丟失

做一個簡單的登入介面卡了半天,在這裡簡單記錄解決辦法  目錄結構如下,jsp介面放在WEB-INF目錄下面 登入介面 點選登入之後沒有判斷是否正確,直接通過spring-mvc跳轉到hello.jsp介面 跳轉後js和css樣式丟失   更改後的介面 sprin

Spring Boot開發一個web API 介面返回資料

需求:開發微信小程式無法直接獲取到伺服器資料庫資料,所以需要一個API返回 開發軟體:IntelliJ IDEA 框架:Spring Boot 第一步 新建一個Spri

spring mvc ajax jsonp 跨域請求 返回值 配置

背景: AJAX向後臺(springmvc)傳送請求,報錯:已阻止交叉源請求:同源策略不允許讀取 http://127.0.0.1:8080/DevInfoWeb/getJsonp 上的遠端資源。可 以將資源移動到相同的域名上或者啟用 CORS 來解決這個問題。 百度

Jquery 使用Ajax獲取後臺返回Json數據後,頁面處理

[] object inpu empty 獲取 reader form 取數 oid <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtm

java讀取介面返回json資料 (二)

 註釋文字可以全部刪除, java讀取 其他服務介面 返回的json資料 package cn.wangshiyu777; import java.io.BufferedReader; import java.io.InputStreamReader; import j

Spring Cloud Stream消費失敗後的處理策略(二):定義錯誤處理邏輯

應用場景 上一篇《Spring Cloud Stream消費失敗後的處理策略(一):自動重試》介紹了預設就會生效的訊息重試功能。對於一些因環境原因、網路抖動等不穩定因素引發的問題可以起到比較好的作用。但是對於諸如程式碼本身存在的邏輯錯誤等,無論重試多少次都不可能成功的問題,是無法修復的。對於這樣的情況,前文

JavaEE開發之SpringMVC中的定義攔截器及異常處理

上篇部落格我們聊了《》,本篇部落格我們就聊一下自定義攔截器的實現、以及使用ModelAndView物件將Controller的值載入到JSTL上、最後再聊一下異常的捕獲處理。這些在日常開發中都是經常使用的東西。具體請看下方內容。 一、自定義攔截器 顧名思義,攔截器是負責攔截某些東西的工具。本部分我們建立的攔

Retrofit定義GsonConverter處理請求錯誤異常處理

通常從服務端拿到的JSON資料格式大概如下: { "code":1, "message":"查詢成功", "detail":{"aa":"123","bb":"123","cc":"123"} } 因此通常我們會定義一個實