- 摘要:如何正確的處理API的返回信息,讓返回的錯誤信息提供更多的含義是一個非常值得做的功能。默認一般返回的都是難以理解的堆棧信息,然而這些信息也許對於API的客戶端來說有可能並沒有多大用途,並沒有多大意義。如果我們把錯誤的信息分成多個字段,這樣api客戶端就可以解析這些信息,然后給用戶反饋更好的錯誤message。在本文中,我們就來介紹在我們使用springboot來構建RESTAPI時如何更好的更恰當的處理錯誤信息。使用Spring來構建RESTAPI現在基本上已經變成了java
-
如何正確的處理API的返回信息,讓返回的錯誤信息提供更多的含義是一個非常值得做的功能。
默認一般返回的都是難以理解的堆棧信息,然而這些信息也許對於API的客戶端來說有可能並沒有多大用途,並沒有多大意義。
如果我們把錯誤的信息分成多個字段,這樣api客戶端就可以解析這些信息,然后給用戶反饋更好的錯誤message。
在本文中,我們就來介紹在我們使用spring boot來構建REST API時如何更好的更恰當的處理錯誤信息。
使用Spring來構建REST API現在基本上已經變成了java開發者事實上的標准。
如果你使用Spring Boot的話,就更方便了,因為它幫你搞了很多的樣板代碼,而且通過auto-configuration可以集成各種組件。
我們將假設你在應用此處所述的知識之前已經比較熟悉使用這些技術的API開發的基礎知識。
如果你仍然不確定如何開發基本的REST API,那么你應該先去了解下有關Spring MVC的文章,或者關於構建Spring REST服務的文章。
讓Error響應更清晰
在本文中,我們將使用托管在GitHub(源碼spring-boot-exception-handling在文末的閱讀原文里,鏈接:https://github.com/importsource/spring-boot-exception-handling) 上的spring-boot-exception-handling應用程序上的源代碼來通過REST API來查詢“鳥”這個對象。 代碼里有本文中描述的功能和更多的錯誤處理方案的示例。 以下是該應用程序中實現的幾個endpoint:
Spring框架的MVC模塊提供了一些很好的功能來幫助處理錯誤。 但是, 它把處理異常的事情扔給了開發人員,需要開發人員自己來處理異常,然后向API客戶端返回返回有意義的響應。
我們來看一下Spring Boot的默認做法。 當我們把下面的一個對象通過 HTTP POST 發送到 /bird 端點時,我們故意給“mass”字段傳遞一個字符串“aaa”,其實這個字段類型是一個整數:
然后我 們來看一下 Spring Boot的默認應答。沒有任何額外的錯誤處理:
嗯。。。這個響應信息確實提供了一些不錯的字段,但是它更側重的時候拋出一個底層的變成異常。順便說一句,這是Spring Boot中的DefaultErrorAttributes類。 時間戳字段是一個整數,甚至不攜帶時間戳所在的度量單位的信息。異常字段的話,可能只有Java開發人員看到這個比較開心,這些信息讓API消費者也陷入了思索API服務端究竟發生了什么內部的編程錯誤。 如果我們從這些編程異常的內容中抽象出更多的細節是不是更好一點?那么就讓我們學習下如何正確地處理這些異常,並將它們轉成更好的JSON表示形式,使我們的API客戶端理解起來更加的輕松。
因為我們接下來要使用到Java 8日期和時間類,我們首先就加個Jackson JSR310轉換器的maven依賴。 這樣我們就可以使用@JsonFormat這個注解來將Java 8的日期和時間類轉換為JSON來表示:
好的,來定義一個表示API錯誤的類。 我們將創建一個名為ApiError的類,其具有足夠的字段來保存REST調用期間發生的錯誤的相關信息。
status 屬性:保存操作調用狀態。 比如4xx客戶端錯誤或5xx服務端錯誤。 一個常見的情況是比如http代碼400,表示BAD_REQUEST,這種情況是當客戶端例如發送了格式不正確的字段,比如一個無效的電子郵件地址。
timestamp 屬性:保存發生錯誤的日期時間。
message 屬性:保存有關錯誤的對用戶友好的消息。
debugMessage 屬性:是更詳細的描述錯誤的系統消息。
subErrors 屬性:包含發生的一系列子錯誤。 這用於在單個調用中出現多個錯誤。 比如多個字段驗證失敗的驗證錯誤。ApiSubError類就是用來封裝這一系列子錯誤的。
ApiValidationError擴展了ApiSubError。它表示在REST調用期間遇到的驗證問題的類。
下面,你將看到在我們實現了這里所做的改進之后生成的JSON響應的例子,僅僅是為了了解本文接下來的內容。
下面的例子就是當一個實體沒有找到后返回的樣子(端點:GET /birds/2):
下面是當我們發送一個POST /birds JSON串后,里邊包含了非法的值賦值給了鳥的mass字段,然后返回了如下錯誤信息:
SpringBoot 方式的錯誤處理
接下來我們介紹一下將要用於處理異常的Spring注解。
RestController
RestController是處理REST操作放置在類上的基礎注解。
ExceptionHandler
ExceptionHandler 是一個Spring注解,它提供了一種機制來處理在處理程序執行過程中拋出的異常(比如Controller操作)。 這個注解(如果用於Controller類的話)將用作處理僅在此Controller中拋出的驗證yi'cyi異常。 總而言之,最常用的方法是在@ControllerAdvice類的方法上使用@ExceptionHandler,以便將異常處理應用到所有的Controller或指定的Controller子集上。
ControllerAdvice
ControllerAdvice 是Spring 3.2中引入的一個注解,顧名思義,是“Advice”多個Controller。 你可以啟用一個ExceptionHandler,然后讓多個Controller都遵循它所定義的異常處理。 這樣的話,我們就只需要在一個地方定義好如何處理某一個異常,並且當這個ControllerAdvice涵蓋的類拋出該異常時,這個處理程序就將會被調用。 如果你只是希望某一些Controller受影響,那么你可以通過在@ControllerAdvice上加這幾個選擇器屬性來限制:annotations(),basePackageClasses()和basePackages())。 如果沒有添加這些選擇器,則ControllerAdvice將應用於所有Controller。
所以通過使用@ExceptionHandler和@ControllerAdvice,我們可以定義一個中心點來處理異常,並將它們包裝在一個比Default錯誤處理機制組織錯誤信息更好的ApiError對象中。
處理異常
下一步是創建處理異常的類。 簡單點說,我們稱之為RestExceptionHandler,它繼承自Spring Boot的ResponseEntityExceptionHandler。 然后我們擴展ResponseEntityExceptionHandler,因為它已經提供了Spring MVC異常的一些基本處理,接下來我們就針對一些新的異常添加一些新的handler,算是對現有的handler的一個擴展和改進。
如果你看看ResponseEntityExceptionHandler的源代碼,你會看到很多方法叫做handle ******(),如handleHttpMessageNotReadable()或handleHttpMessageNotWritable()。 我們先來看看,我們是如何通過擴展handleHttpMessageNotReadable()來處理HttpMessageNotReadableException異常們的。 其實只需要在RestExceptionHandler類中覆蓋方法handleHttpMessageNotReadable()如下:
之前已說過,如果HttpMessageNotReadableException被拋出,錯誤的message將是“ Malformed JSON request( 格式錯誤的JSON請求)”,並且該錯誤將被封裝在ApiError對象內。
下面就是我們擴展后的REST調用的響應json:
處理自定義異常
接下來介紹如何創建一個方法來處理在Spring Boot的ResponseEntityExceptionHandler中沒有被聲明處理的異常。
一個用於處理數據庫調用的Spring應用程序的常見場景就是使用存儲庫類通過其ID來查找一條或多條記錄。 但是,我們發現CrudRepository.findOne()方法中,如果找不到數據,那么就返回null。 這意味着如果我們的服務只是調用該方法並直接返回給Controller,即使沒有找到資源,我們也會得到HTTP代碼 200(OK)。 事實上,正確的方法應該是返回HTTP / 1.1規范中指定的HTTP code 404(NOT FOUND)。
為了處理這種情況,我們可以創建一個名為EntityNotFoundException的自定義異常。 這是一個自定義創建的異常,與javax.persistence.EntityNotFoundException不同 ,因為它提供了一些緩解對象創建的構造函數,並且可以選擇以 不同方式處理javax.persistence異常。
然后,我們在RestExceptionHandler類中為這個新創建的EntityNotFoundException創建一個ExceptionHandler。 其實就是創建一個名為handleEntityNotFound()的方法,並使用@ExceptionHandler對其進行注解,將類對象EntityNotFoundException.class傳遞給它。 這表明Spring每次拋出EntityNotFoundException時,Spring應該調用此方法來處理它。 當使用@ExceptionHandler注解方法時,它將接受多種自動注入的參數,如WebRequest,Locale和其他。 下面我們就把異常EntityNotFoundException本身作為下面這個handleEntityNotFound方法的參數。
好! 在handleEntityNotFound()方法中,我們將HTTP狀態代碼設置為NOT_FOUND並使用了新的異常消息。 以下是 GET /birds/2 終端的響應:
總結
控制異常處理非常重要,所以我們可以將這些異常映射到ApiError對象,然后向API客戶端提供了更有意義的信息,讓客戶端知道發生了什么。 然后就是如何為應用程序代碼中拋出的異常創建更多的手工方法(帶有@ExceptionHandler的方法)。 還有一些其他常見異常的例子,例如MethodArgumentTypeMismatchException,ConstraintViolationException等等。
https://cn.aliyun.com/jiaocheng/778288.html