SpringBoot構建RESTful API——處理返回異常
@ResponseBody 提供了一種很有用的方式,能夠將控制器返回的 Java 物件轉換為傳送到客戶端的資源表述。
一個好的 REST API 不僅僅能夠在客戶端和伺服器之間傳遞資源,他還能夠給客戶端提供額外的資料,幫助客戶端理解資源或者在請求中發生了什麼情況。
傳送錯誤資訊到客戶端
例如,我們為 UserController 中新增一個新的處理器方法,它會提供單個 Spittle 物件。
@RestController
@RequestMapping("/user")
public class UserController {
Map<String, User> users = Collections.synchronizedMap(new HashMap<String, User>());
@RequestMapping(value = "/{id}",method = RequestMethod.GET, produces = "application/json")
public User getUserById (@PathVariable("id") String id){
//初始化使用者資訊
initUserInfo();
return users.get(id);
}
private void initUserInfo() {
User user1 = new User("123","zhaoben");
users.put(user1.getId(),user1);
}
}
以上程式碼中,利用 Map 模擬資料庫查詢操作,通過id(123)進行查詢,然後他根據呼叫 getUserById() 方法,查詢對應 User 物件。 處理器方法返回查詢結果。訊息轉換器會負責產生客戶端所需要的資源表述。
*如果,根據給定 id,無法找到某個 User 物件,則 getUserById 返回 null
的同時,HTTP 狀態碼為 200,所有的過程看起來都很合理。但是,所有的事情都是不對的,客戶端要求 User 物件,但是它什麼都沒有得到。客戶端既沒有收到 User 物件,也沒有收到任何訊息表示出現了錯誤。*
Spring提供了以下幾種方式來處理這樣的場景。
1. 使用 @ResponseStatus 註解可以指定狀態碼;
2. 控制器方法可以返回 ResponseEntity 物件,該物件包含更多相關的元資料;
3. 異常處理器能夠應對錯誤場景,這樣處理器方法就能關注正常狀況;
使用 ResponseEntity
ResponseEntity 中包含了響應相關的元資料(頭部資訊和狀態碼)以及要轉換成資源表述的物件。
新增狀態碼
所以當無法找到 User 物件的時候,我們可以返回 HTTP 404 錯誤。
@RequestMapping(value = "/{id}",method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<User> getUserById (@PathVariable("id") String id){
//初始化使用者資訊
initUserInfo();
User result = users.get(id);
HttpStatus status = result != null ? HttpStatus.OK : HttpStatus.NOT_FOUND;
return new ResponseEntity<User>(result,status);
}
我們在正確的方向上走出了第一步,如果所要求的 User 無法找到,客戶端就能就受到一個合適的狀態碼。但是在本例中,響應體依然為空。我們可能會希望在響應體中包含一些錯誤資訊
新增響應體
首先定義一個包含錯誤資訊的 Error 物件
public class Error {
private int code;
private String message;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
然後,我們可以修改 getById(), 讓它返回 Error
@RequestMapping(value = "/{id}",method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<?> getUserById (@PathVariable("id") String id){
//初始化使用者資訊
initUserInfo();
User result = users.get(id);
HttpStatus status = result != null ? HttpStatus.OK : HttpStatus.NOT_FOUND;
if(result == null){
Error error = new Error(4 , "User ("+id+") not found");
return new ResponseEntity<Error>(error, status);
}
return new ResponseEntity<User>(result,status);
}
你可以發現,雖然問題的到了解決,但是程式碼卻變得更加複雜,涉及到更多的邏輯,包括條件語句。另外,方法返回 ResponseEntity
public class UserNotFountException extends RuntimeException{
private String userId;
public UserNotFountException(String userId) {
this.userId = userId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
}
接著,定義能夠對應的 UserNotFountException 的錯誤處理器
@ExceptionHandler(UserNotFountException.class)
public ResponseEntity<Error> UserNotFound(UserNotFountException e){
String userId = e.getUserId();
Error error = new Error(4 , "User ("+userId+") not found");
return new ResponseEntity<Error>(error,HttpStatus.NOT_FOUND);
}
@ExceptionHandler 註解能夠用到控制器方法中,用來處理特定的異常。
現在,我們可以修改 getUserById() 方法
@RequestMapping(value = "/{id}",method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<User> getUserById (@PathVariable("id") String id){
//初始化使用者資訊
initUserInfo();
User result = users.get(id);
HttpStatus status = result != null ? HttpStatus.OK : HttpStatus.NOT_FOUND;
if(result == null){
throw new UserNotFountException(id);
}
return new ResponseEntity<User>(result,status);
}
在響應中設定頭部資訊