使用 Spring Boot 提供API錯誤訊息的好方式
對於 API 使用者來說,API 提供有用的錯誤訊息非常重要。否則,很難弄清楚為什麼事情不起作用。與在伺服器端實際實現有用的錯誤響應相比,除錯錯誤可能會很快成為客戶端更大的工作。如果客戶無法自己解決問題並且需要額外的溝通,則尤其如此。
儘管如此,這個話題經常被忽視或三心二意地實施。
客戶端和安全形度
關於錯誤訊息有不同的觀點。詳細的錯誤訊息對客戶端更有幫助,而從安全形度來看,最好公開儘可能少的資訊。幸運的是,如果正確實施,這兩種觀點通常不會發生太大沖突。
如果錯誤是由客戶產生的,那麼客戶通常對非常具體的錯誤訊息感興趣。這通常應由4xx 狀態程式碼指示。在這裡,我們需要在不暴露任何內部實現細節的情況下指向客戶端所犯錯誤的特定訊息。
另一方面,如果客戶端請求有效並且錯誤是由伺服器產生的(5xx 狀態程式碼),我們應該對錯誤訊息持保守態度。在這種情況下,客戶端無法解決問題,因此不需要有關錯誤的任何詳細資訊。
指示錯誤的響應應至少包含兩件事:人類可讀的訊息和錯誤程式碼。第一個幫助在日誌檔案中看到錯誤訊息的開發人員。後者允許在客戶端上進行特定的錯誤處理(例如,向使用者顯示特定的錯誤訊息)。
如何在 Spring Boot 應用程式中構建有用的錯誤響應?
假設我們有一個可以釋出文章的小應用程式。執行此操作的簡單 Spring 控制器可能如下所示:
@RestController public class ArticleController { @Autowired private ArticleService articleService; @PostMapping("/articles/{id}/publish") public void publishArticle(@PathVariable ArticleId id) { articleService.publishArticle(id); } } |
這裡沒什麼特別的,控制器只是將操作委託給一個服務,它看起來像這樣:
@Service public class ArticleService { @Autowired private ArticleRepository articleRepository; public void publishArticle(ArticleId id) { Article article = articleRepository.findById(id) .orElseThrow(() -> new ArticleNotFoundException(id)); if (!article.isApproved()) { throw new ArticleNotApprovedException(article); } ... } } |
在服務內部,我們針對可能的客戶端錯誤丟擲特定異常。請注意,這些異常不僅僅描述了錯誤。它們還攜帶可能有助於我們稍後生成良好錯誤訊息的資訊:
public class ArticleNotFoundException extends RuntimeException { private final ArticleId articleId; public ArticleNotFoundException(ArticleId articleId) { super(String.format("No article with id %s found", articleId)); this.articleId = articleId; } // getter } |
如果異常足夠具體,我們不需要通用訊息引數。相反,我們可以在異常建構函式中定義訊息。
接下來我們可以在@ControllerAdvice bean 中使用@ExceptionHandler方法來處理實際的異常:
@ControllerAdvice public class ArticleExceptionHandler { @ExceptionHandler(ArticleNotFoundException.class) public ResponseEntity<ErrorResponse> onArticleNotFoundException(ArticleNotFoundException e) { String message = String.format("No article with id %s found", e.getArticleId()); return ResponseEntity .status(HttpStatus.NOT_FOUND) .body(new ErrorResponse("ARTICLE_NOT_FOUND", message)); } ... } |
如果控制器方法丟擲異常,Spring 會嘗試找到一個用匹配的@ExceptionHandler註釋來註釋的方法。@ExceptionHandler方法可以有靈活的方法簽名,類似於標準控制器方法。例如,我們可以給一個HttpServletRequest請求引數,Spring 會傳入當前的請求物件。可能的引數和返回型別在@ExceptionHandler的Javadocs 中描述。
在這個例子中,我們建立了一個簡單的ErrorResponse物件,它包含一個錯誤程式碼和一條訊息。
訊息是根據異常攜帶的資料構造的。也可以將異常訊息傳遞給客戶端。但是,在這種情況下,我們需要確保團隊中的每個人都知道這一點,並且異常訊息不包含敏感資訊。否則,我們可能會不小心將內部資訊洩露給客戶端。
ErrorResponse是一個用於 JSON 序列化的簡單 Pojo:
public class ErrorResponse { private final String code; private final String message; public ErrorResponse(String code, String message) { this.code = code; this.message = message; } // getter } |
測試錯誤響應
一個好的測試套件不應錯過針對特定錯誤響應的測試。在我們的示例中,我們可以用不同的方式驗證錯誤行為。一種方法是使用Spring MockMvc測試。
@SpringBootTest @AutoConfigureMockMvc public class ArticleExceptionHandlerTest { @Autowired private MockMvc mvc; @MockBean private ArticleRepository articleRepository; @Test public void articleNotFound() throws Exception { when(articleRepository.findById(new ArticleId("123"))).thenReturn(Optional.empty()); mvc.perform(post("/articles/123/publish")) .andExpect(status().isNotFound()) .andExpect(jsonPath("$.code").value("ARTICLE_NOT_FOUND")) .andExpect(jsonPath("$.message").value("No article with id 123 found")); } } |
在這裡,我們使用一個模擬的 ArticleRepository,它為傳遞的 id返回一個空的Optional。然後我們驗證錯誤程式碼和訊息是否與預期的字串匹配。
總結
有用的錯誤訊息是 API 的重要組成部分。
如果客戶端產生錯誤(HTTP 4xx 狀態程式碼),伺服器應提供至少包含錯誤程式碼和人類可讀錯誤訊息的描述性錯誤響應。對意外伺服器錯誤 (HTTP 5xx) 的響應應該是保守的,以避免意外暴露任何內部資訊。
為了提供有用的錯誤響應,我們可以使用攜帶相關資料的特定異常。在@ExceptionHandler方法中,我們然後根據異常資料構造錯誤訊息。
相關文章
- 使用SAP WebIDE消費API Portal裡建立的API時的錯誤訊息WebIDEAPI
- 提供統一的錯誤APIAPI
- spring boot使用Java並行流傳送kafka訊息報錯Spring BootJava並行Kafka
- Spring Boot:使用Rabbit MQ訊息佇列Spring BootMQ佇列
- 使用Spring Boot實現訊息佇列Spring Boot佇列
- Spring Boot系列十七 Spring Boot 整合 websocket,使用RabbitMQ做為訊息代理Spring BootWebMQ
- Spring boot/Spring 統一錯誤處理方案的使用Spring Boot
- Apache Kafka和Spring Boot的容錯和可靠訊息傳遞 – Arnold GalovicsApacheKafkaSpring Boot
- SAP錯誤訊息除錯之七種武器:讓所有的錯誤訊息都能被定位除錯
- Spring Boot和Apache Kafka結合實現錯誤處理,訊息轉換和事務支援?Spring BootApacheKafka
- Spring Boot 參考指南(訊息傳遞)Spring Boot
- Angular No provider for EffectsRootModule錯誤訊息AngularIDE
- Go 語言的錯誤訊息處理Go
- spring boot 全域性錯誤處理Spring Boot
- Spring Boot系列20 Spring Websocket實現向指定的使用者傳送訊息Spring BootWeb
- Spring Boot 整合 RabbitMQ 傳送延時訊息Spring BootMQ
- Spring Boot系列十六 WebSocket簡介和spring boot整合簡單訊息代理Spring BootWeb
- 如何在spring boot 使用 gitlab的ApiSpring BootGitlabAPI
- 七、Spring Boot 錯誤處理原理 & 定製錯誤頁面Spring Boot
- Spring Boot返回靜態錯誤頁面Spring Boot
- 如何除錯ucenter中傳送的api通知訊息除錯API
- Spring Boot 中使用 Java API 呼叫 ElasticsearchSpring BootJavaAPIElasticsearch
- Spring Boot 中使用 Java API 呼叫 luceneSpring BootJavaAPI
- Windows下使用python庫 curses遇到錯誤訊息的解決方案WindowsPython
- Spring Boot中@Import三種使用方式!Spring BootImport
- spring boot使用註解的方式整合mybaitsSpring BootAI
- python 用traceback列印錯誤訊息(轉)Python
- Laravel 的 Validation 檢索驗證錯誤訊息Laravel
- 使用 rocketmq-spring-boot-starter 來配置、傳送和消費 RocketMQ 訊息MQSpringboot
- Spring Boot中使用WebSocket總結(三):使用訊息佇列實現分散式WebSocketSpring BootWeb佇列分散式
- 關於 SAP UI5 getSAPLogonLanguage is not a function 的錯誤訊息以及 API 版本的討論UIGoFunctionAPI
- Spring Boot配置是遇到錯誤:jdbcUrl is required with driverClassNameSpring BootJDBCUI
- Spring Boot配置錯誤:Failed to determine a suitable driver classSpring BootAIUI
- Spring Boot中的 6 種API請求引數讀取方式Spring BootAPI
- 6、Sping Boot訊息boot
- 使用JBang構建Spring Boot Rest API教程Spring BootRESTAPI
- Spring Boot 樂觀鎖加鎖失敗 - 使用AOP恢復錯誤Spring Boot
- 如何使用訊息佇列、Spring Boot和Kubernetes擴充套件微服務佇列Spring Boot套件微服務