5

當我有JSR-303(驗證框架)的註釋如下模型:彈簧引導結合和REST控制器處理驗證錯誤

public enum Gender { 
    MALE, FEMALE 
} 

public class Profile { 
    private Gender gender; 

    @NotNull 
    private String name; 

    ... 
} 

和以下JSON數據:

{ "gender":"INVALID_INPUT" } 

在我的REST控制器,我想要處理綁定錯誤(gender屬性的無效枚舉值)和驗證錯誤(name屬性不能爲空)。

下面的控制器方法不起作用:

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

這給com.fasterxml.jackson.databind.exc.InvalidFormatException序列化錯誤結合或驗證發生之前。

一些擺弄後,我想出了這個定製的代碼做什麼,我想:

@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. 

    ... 
} 

基本上這代碼做什麼,是序列化到一個通用的地圖,而不是模型,然後使用自定義代碼綁定建模並檢查錯誤。

我有以下問題:

  1. 是自定義代碼去這裏的路還是有春季啓動這樣做的更標準的方式?
  2. @Validated註釋是如何工作的?我怎樣才能使自己的自定義註釋像@Validated一樣封裝我的自定義綁定代碼?

回答

1

通常當彈簧MVC無法讀取HTTP消息(例如請求體),它會拋出異常HttpMessageNotReadableException的一個實例。所以,如果spring不能綁定到你的模型,它應該拋出這個異常。另外,如果您確實在不是在您的方法參數中的每個待驗證模型之後定義了一個BindingResult,如果出現驗證錯誤,那麼spring將拋出異常MethodArgumentNotValidException。有了這一切,您可以創建ControllerAdvice,捕獲這兩個異常並以您想要的方式處理它們。

@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

這裏缺點是,當出現綁定錯誤你沒有得到BindingResult。即您可以對'MethodArgumentNotValidException'異常執行'ex.getBindingResult()',但不會出現'HttpMessageNotReadableException'異常。 –

+0

後者似乎是合理的,因爲當綁定失敗時,我們無法獲得綁定結果。沒有約束力。 –

+0

在我看來,將一個字符串放在一個int字段或一個錯誤的Enum值中的綁定錯誤應該被視爲驗證錯誤。在'BindingResult'中使用'DataBinder'獨立的綁定字段錯誤,這樣服務可以返回更詳細的錯誤響應。 –

3

這是我在我的項目在春季開機驗證REST API的一個已經使用的代碼,這是不一樣的,你要求的,但相同的。檢查如果這有助於

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

錯誤。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; 
} 

} 

你也可以看看這裏:Spring REST Validation