1. 程式人生 > >SpringBoot構建RESTful API——處理返回異常

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);
}

在響應中設定頭部資訊