5

Khi tôi có mô hình sau đây với JSR-303 (khung xác nhận) chú thích:Xuân Boot ràng buộc và xử lý lỗi xác nhận trong điều khiển REST của

public enum Gender { 
    MALE, FEMALE 
} 

public class Profile { 
    private Gender gender; 

    @NotNull 
    private String name; 

    ... 
} 

và các dữ liệu sau JSON:

{ "gender":"INVALID_INPUT" } 

Trong bộ điều khiển REST của tôi, tôi muốn xử lý cả hai lỗi liên kết (giá trị enum không hợp lệ cho thuộc tính gender) và các lỗi xác thực (name thuộc tính không thể rỗng).

Phương pháp điều khiển sau đây KHÔNG làm việc:

@RequestMapping(method = RequestMethod.POST) 
public Profile insert(@Validated @RequestBody Profile profile, BindingResult result) { 
    ... 
} 

Điều này cho phép com.fasterxml.jackson.databind.exc.InvalidFormatException lỗi serialization trước khi ràng buộc hoặc xác nhận diễn ra.

Sau khi một số không quan trọng, tôi đã đưa ra mã tùy chỉnh này mà làm những gì tôi muốn:

@RequestMapping(method = RequestMethod.POST) 
public Profile insert(@RequestBody Map values) throws BindException { 

    Profile profile = new Profile(); 

    DataBinder binder = new DataBinder(profile); 
    binder.bind(new MutablePropertyValues(values)); 

    // validator is instance of LocalValidatorFactoryBean class 
    binder.setValidator(validator); 
    binder.validate(); 

    // throws BindException if there are binding/validation 
    // errors, exception is handled using @ControllerAdvice. 
    binder.close(); 

    // No binding/validation errors, profile is populated 
    // with request values. 

    ... 
} 

Về cơ bản những gì mã này không được serialize đến một bản đồ chung thay vì mô hình và sau đó sử dụng mã tùy chỉnh để ràng buộc để lập mô hình và kiểm tra lỗi.

Tôi có câu hỏi sau:

  1. là mã tùy chỉnh con đường để đi đây hoặc là có một cách tiêu chuẩn hơn để làm điều này trong mùa xuân Boot?
  2. Cách hoạt động của chú thích @Validated? Làm cách nào để tạo chú thích tùy chỉnh của riêng tôi hoạt động như @Validated để đóng gói mã ràng buộc tùy chỉnh của tôi?

Trả lời

1

Thông thường khi Spring MVC không đọc được thông báo http (ví dụ: yêu cầu nội dung), nó sẽ ném một trường hợp ngoại lệ HttpMessageNotReadableException. Vì vậy, nếu mùa xuân không thể liên kết với mô hình của bạn, nó sẽ ném ngoại lệ đó. Ngoài ra, nếu bạn làm KHÔNG xác định BindingResult sau mỗi mô hình được xác thực trong thông số phương pháp của bạn, trong trường hợp lỗi xác thực, mùa xuân sẽ ném một ngoại lệ MethodArgumentNotValidException. Với tất cả điều này, bạn có thể tạo ra ControllerAdvice mà bắt hai trường hợp ngoại lệ và xử lý chúng theo cách mong muốn của bạn.

@ControllerAdvice(annotations = {RestController.class}) 
public class UncaughtExceptionsControllerAdvice { 
    @ExceptionHandler({MethodArgumentNotValidException.class, HttpMessageNotReadableException.class}) 
    public ResponseEntity handleBindingErrors(Exception ex) { 
     // do whatever you want with the exceptions 
    } 
} 
+1

Nhược điểm ở đây là bạn không nhận được BindingResult khi một lỗi ràng buộc xảy ra. I E. bạn có thể làm 'ex.getBindingResult()' trên 'MethodArgumentNotValidException' ngoại lệ, nhưng không phải trên một ngoại lệ' HttpMessageNotReadableException'. –

+0

Sau này có vẻ hợp lý, bởi vì khi ràng buộc không thành công, chúng ta không thể có kết quả ràng buộc. Không có ràng buộc. –

+0

Trong quan điểm của tôi ràng buộc lỗi như đặt một String trong một lĩnh vực int hoặc một giá trị Enum sai nên được coi là lỗi xác nhận. Việc sử dụng các lỗi trường độc lập 'DataBinder' cũng có ràng buộc trong trường' BindingResult' để dịch vụ có thể trả về một phản hồi lỗi chi tiết hơn. –

1

Tôi đã từ bỏ điều này; nó chỉ là không thể có được các lỗi ràng buộc bằng cách sử dụng @RequestBody mà không có nhiều mã tùy chỉnh. Điều này khác với các bộ điều khiển liên kết với các đối số JavaBeans thuần túy vì @RequestBody sử dụng Jackson để liên kết thay vì bộ dữ liệu Spring.

Xem https://jira.spring.io/browse/SPR-6740?jql=text%20~%20%22RequestBody%20binding%22

3

Đây là mã những gì tôi đã được sử dụng trong một trong những dự án của tôi để phê chuẩn API REST trong khởi động mùa xuân, điều này không giống như bạn yêu cầu, nhưng là giống hệt nhau .. kiểm tra nếu điều này giúp

@RequestMapping(value = "/person/{id}",method = RequestMethod.PUT) 
@ResponseBody 
public Object updatePerson(@PathVariable Long id,@Valid Person p,BindingResult bindingResult){ 
    if (bindingResult.hasErrors()) { 
     List<FieldError> errors = bindingResult.getFieldErrors(); 
     List<String> message = new ArrayList<>(); 
     error.setCode(-2); 
     for (FieldError e : errors){ 
      message.add("@" + e.getField().toUpperCase() + ":" + e.getDefaultMessage()); 
     } 
     error.setMessage("Update Failed"); 
     error.setCause(message.toString()); 
     return error; 
    } 
    else 
    { 
     Person person = personRepository.findOne(id); 
     person = p; 
     personRepository.save(person); 
     success.setMessage("Updated Successfully"); 
     success.setCode(2); 
     return success; 
    } 

Success.java

public class Success { 
int code; 
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; 
} 
} 

Lỗi.java

public class Error { 
int code; 
String message; 
String cause; 

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

public String getCause() { 
    return cause; 
} 

public void setCause(String cause) { 
    this.cause = cause; 
} 

} 

Bạn cũng có thể có một cái nhìn ở đây: Spring REST Validation

Các vấn đề liên quan