Sử dụng Spring ResponseStatusException
Giới thiệu
Một ứng dụng RESTful, bằng cách trả về các HTTP status code trong HTTP response nó có thể thông báo về sự thành công hay thất bại của một HTTP request. Ví dụ như nếu người dùng request lên một id không hề tồn tại, các HTTP status code có thể giúp xác định được các vấn đề có thể xảy ra khi xử lí request.
Trong Spring chúng ta cũng có rất nhiều cách để đặt HTTP status code cho một HTTP response. Tuy nhiên trong bài viết này mình sẽ giới thiệu về một class mới được giới thiệu trong Spring 5 đó chính là ResponseStatusException sẽ hỗ trợ cho ta việc áp dụng HTTP status code.
@ResponseStatus
Trước khi tìm hiểu về ResponseStatusException , ta sẽ tìm hiểu qua về @ResponseStatus annotation. Annotation này được giới thiệu trong Spring 3 để giải quyết vấn đề áp dụng HTTP status code cho HTTP response.
Với annotaion này chúng ta sẽ sử dụng để định nghĩa status code và reason cho HTTP response:
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Image not found")
public class ImageNotFoundException extends Exception{
<em>//...</em>
}
Ở trong ví dụ này, nếu Exception được đưa ra trong một xử lí HTTP request, thì trong response sẽ bao gồm HTTP status code được chỉ định sẽ là 404.
Tuy nhiên với phương pháp sử dụng @ResponseStatus ta có một nhược điểm là nó sẽ tạo ra mối quan hệ phụ thuộc chặt chẽ vói Exception. Và như trường hợp ta xét ở trên thì tất cả các Exception có kiểu ImageNotFoundException khi được đưa ra thì đều cho ta một response với thông báo lỗi và status code là như nhau trong mọi trường hợp.
ResponseStatusException
ResponseStatusException được tạo ra nhằm thay thế cho @ResponseStatus và là một base class cho các exception được sử dụng đề apply các HTTP status cho response. Và đây cũng là một RuntimeException.
Với ResponseStatusException class sẽ cung cấp cho ta 3 contructor:
Với các đối số truyền vào cho contructor method:
- status – HTTP status được set cho HTTP response
- reason – message được hiển thị để giải thích cho exception
- cause – là một java.lang.Throwable nguyên nhân của ResponseStatusException
Những điểm mạnh cảu việc dùng ResponseStatusException class:
- Thứ nhất, việc khai báo và sử dụng dễ dàng
- Thứ hai, các exception cùng loại ta có thể xử lí riêng biệt và có thể linh hoạt các stutas code khác nhau có thể được set trong response, giảm sự phụ thuộc vào nhau.
- Thứ ba, ta có thể tránh được việc phải tạo các class exception không cần thiết.
- Và cuối cùng, class cấp cho ta nhiều quyền hơn trong việc xử lí exception, vì các exception được tạo theo chương trình nên được kiểm soát tốt hơn.
Ví dụ
Bây giờ, hãy xem một ví dụ về cách sử dụng ResponseStatusException trong thực tế:
Ta có 1 class ToDoController khái báo “/todo” mapping truyền vào tham số id để lấy ra Todo object tương ứng
@RestController
public class ToDoController {
@Autowired
private ToDoService toDoService;
@GetMapping(value = "/todo")
public ToDo getTodo(@RequestParam(value = "id", required = false) Long id) {
try {
return toDoService.getTodo(id);
} catch (TodoNotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Todo not found", e);
}
}
}
Với một exception được khai báo
public class TodoNotFoundException extends Exception{
public TodoNotFoundException(String errorMessage){
super(errorMessage);
}
}
Spring sẽ cung cấp cho ta một “/error” mapping sẽ trả về response dưới định dạng JSON với HTTP status. Trong ví dụ này khi truyền vào id không hề tồn tại chương trình sẽ trả về ResponseStatusException với response chứa status code tương ứng.
Đây sẽ là response trong trường hợp có exception (ta sẽ dùng Postman để gửi request):
Để xem được message về lỗi trong response ta sẽ thêm thuộc tính server.error.include-message=always
. Khi đó response trả về sẽ có nội dung:
{
"timestamp": "2021-08-03T01:35:20.878+00:00",
"status": 404,
"error": "Not Found",
"message": "Todo not found",
"path": "/todo"
}
Ngoài ra với lợi ích là tính linh hoạt khi có thể gán các status code khác nhau cho cùng một exception ta có thể thứ với một ví dụ khác:
@GetMapping(value = "/todo")
public ToDo getTodo(@RequestParam(value = "id", required = false) Long id) {
try {
return toDoService.getTodo(id);
} catch (TodoNotFoundException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Provide correct Todo Id", e);
}
}
Và response trả về sẽ là:
{
"timestamp": "2021-08-03T01:57:41.502+00:00",
"status": 400,
"error": "Bad Request",
"message": "Provide correct Todo Id",
"path": "/todo"
}
Lời kết
Như vậy trong bài viết này, chúng ta đã cùng tìm hiểu về ResponseStatusException – một cách tốt hơn để tạo một HTTP status code trong HTTP response so với annotation @ResponseStatus. Tuy nhiên với việc nên thận trọng với việc sử dụng vì nếu không có phương pháp xử lí exception thống nhất thì việc thực thi một số quy ước trên toàn ứng dụng sẽ khó khăn và có thể xảy ra code bị trùng lặp.
Cảm ơn các bạn đã đọc bài viết!
Tài liệu tham khảo: https://www.baeldung.com/spring-response-status-exception