Exception handling

Một http request thường có những mã response phổ biến như sau

Mã 2xx: Thành công

Mã 3xx: Định hướng

Mã 4xx: Lỗi phía client

Mã 5xx: Lỗi phía server

Việc handle lỗi ở server nhằn tới hai mục đích:

Giả sử rằng ta có controller BookController như sau:

import com.voquanghoa.bookstore.models.Book;
import com.voquanghoa.bookstore.repositories.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Optional;

@RestController
@RequestMapping("/api/books")
public class BookController {

    @Autowired
    private BookRepository bookRepository;

    @GetMapping("/{id}")
    Optional<Book> get(@PathVariable int id){
        return bookRepository.findById(id);
    }

    @DeleteMapping("/{id}")
    void delete(@PathVariable int id){
        bookRepository.deleteById(id);
    }
}

Thực thi request DELETE với id book 2222 không tồn tại, ta nhận được response code 500 và response chứa thông tin về lỗi

Error handling

Với request GET cũng với id đó, ta được response code 200 và body rỗng

Error handling

Cả hai trường hợp trên đều không hợp lý, ta cần response mã lỗi 404 và body có thể rỗng hoặc json về lỗi.

Để làm điều đó, ta phải implement exception handle như sau

Tạo package exceptions với cấu trúc

Exceptions

File NotFoundException.java

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {
    public NotFoundException(String reason){
        super(reason);
    }
}

File GlobalControllerExceptionHandler.java

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.ServletWebRequest;

@Data
@AllArgsConstructor(access = AccessLevel.PUBLIC)
class ErrorModel{
    private String message;
    private String path;
}

@ControllerAdvice
@ConditionalOnProperty(prefix = "app", name = "disable-default-exception-handling")
class GlobalControllerExceptionHandler {

    public ResponseEntity<ErrorModel> handleException(NotFoundException ex, ServletWebRequest request) {
        return new ResponseEntity<>(new ErrorModel(ex.getMessage(), request.getRequest().getRequestURI()), HttpStatus.NOT_FOUND);
    }
}

Cập nhật BookController.java

@GetMapping("/{id}")
Book get(@PathVariable int id){
    Optional<Book> optionalBook = bookRepository.findById(id);

    if(optionalBook.isPresent()){
        return optionalBook.get();
    }

    throw new NotFoundException(String.format("Book id %d not found", id));
}

@DeleteMapping("/{id}")
void delete(@PathVariable int id){

    if(!bookRepository.existsById(id)){
        throw new NotFoundException(String.format("Book id %d not found", id));
    }

    bookRepository.deleteById(id);
}

Kết quả:

Error handling

Http Response Code = 404

Http Response Body:

{
    "timestamp": "2019-04-07T12:45:31.001+0000",
    "status": 404,
    "error": "Not Found",
    "message": "Book id 2223 not found",
    "path": "/api/books/2223"
}

Trang chủ