2015-12-01 34 views
5

春天驗證在控制器方法勢必收藏RequestBody參數

的實體:

package org.ibp.soq; 

public class MyEntity { 

    private String field1; 
    private String field2; 

    //..getters and setters 

} 

驗證的實體:

package org.ibp.soq; 

import org.springframework.stereotype.Component; 
import org.springframework.validation.Errors; 
import org.springframework.validation.Validator; 

@Component 
public class MyEntityValidator implements Validator { 

    @Override 
    public boolean supports(Class<?> clazz) { 
     return MyEntity.class.equals(clazz); 
    } 

    @Override 
    public void validate(Object target, Errors errors) { 
     MyEntity myEntity = (MyEntity) target; 
     // Logic to validate my entity 
     System.out.print(myEntity); 
    } 

} 

其餘控制器批量put方法:

package org.ibp.soq; 

import java.util.List; 

import javax.validation.Valid; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.web.bind.WebDataBinder; 
import org.springframework.web.bind.annotation.InitBinder; 
import org.springframework.web.bind.annotation.RequestBody; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
import org.springframework.web.bind.annotation.RestController; 

@RestController 
@RequestMapping("/myEntity") 
public class MyEntityRestResource { 

    @Autowired 
    private MyEntityValidator myEntityValidator; 

    @InitBinder 
    protected void initBinder(final WebDataBinder binder) { 
     binder.addValidators(this.myEntityValidator); 
    } 

    @RequestMapping(method = RequestMethod.PUT) 
    public void bulkCreate(@RequestBody @Valid List<MyEntity> myEntities) { 
     // Logic to bulk create entities here. 
     System.out.print(myEntities); 
    } 
} 

當我做一個PUT請求,該資源與下列的請求正文:

[ 
    { 
    "field1": "AA", 
    "field2": "11" 
    }, 

    { 
    "field1": "BB", 
    "field2": "22" 
    } 
] 

我得到的錯誤是:

"Invalid target for Validator [[email protected]]: [[email protected], [email protected]]" 

我可以理解,這是因爲MyEntityValidator「支持」單個MyEntity驗證,不合法爲ArrayList<MyEntity>

MyEntityValidator如果我在請求正文中有單個MyEntity對象,並且參數@RequestBody @Valid MyEntity myEntity有對應的控制器方法,則完美工作。

如何驗證器設置我已經使用,擴展爲支持收集MyEntity的驗證?

回答

1

正如您可能已經猜到的,使用Spring Validation無法實現這一點。 Spring驗證實現了Bean驗證(JSR 303/349),而不是對象驗證。不幸的是,一個集合不是一個Java Bean。你有兩個選擇

  • 手工包裝你的列表中的Java Bean
  • 呼叫內驗證您的批量創建方法myEntityValidator. validate(targetObject, errors)
10

的解決方案是創建用於Collection定製Validator@ControllerAdvice,其登記在WebDataBindersValidator

驗證:

import java.util.Collection; 

import org.springframework.validation.Errors; 
import org.springframework.validation.ValidationUtils; 
import org.springframework.validation.Validator; 
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 

/** 
* Spring {@link Validator} that iterates over the elements of a 
* {@link Collection} and run the validation process for each of them 
* individually. 
* 
* @author DISID CORPORATION S.L. (www.disid.com) 
*/ 
public class CollectionValidator implements Validator { 

    private final Validator validator; 

    public CollectionValidator(LocalValidatorFactoryBean validatorFactory) { 
    this.validator = validatorFactory; 
    } 

    @Override 
    public boolean supports(Class<?> clazz) { 
    return Collection.class.isAssignableFrom(clazz); 
    } 

    /** 
    * Validate each element inside the supplied {@link Collection}. 
    * 
    * The supplied errors instance is used to report the validation errors. 
    * 
    * @param target the collection that is to be validated 
    * @param errors contextual state about the validation process 
    */ 
    @Override 
    @SuppressWarnings("rawtypes") 
    public void validate(Object target, Errors errors) { 
    Collection collection = (Collection) target; 
    for (Object object : collection) { 
     ValidationUtils.invokeValidator(validator, object, errors); 
    } 
    } 
} 

ControllerAdvice:

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 
import org.springframework.web.bind.WebDataBinder; 
import org.springframework.web.bind.annotation.ControllerAdvice; 
import org.springframework.web.bind.annotation.InitBinder; 

/** 
* Controller advice that adds the {@link CollectionValidator} to the 
* {@link WebDataBinder}. 
* 
* @author DISID CORPORATION S.L. (www.disid.com) 
*/ 
@ControllerAdvice 
public class ValidatorAdvice { 

    @Autowired 
    protected LocalValidatorFactoryBean validator; 


    /** 
    * Adds the {@link CollectionValidator} to the supplied 
    * {@link WebDataBinder} 
    * 
    * @param binder web data binder. 
    */ 
    @InitBinder 
    public void initBinder(WebDataBinder binder) { 
    binder.addValidators(new CollectionValidator(validator)); 
    } 
} 
+0

謝謝!我一定會嘗試這種方法。 –

+2

使用\ @ControllerAdvice將應用CollectionValidator到所有控制器。如果在非集合對象上有另一個\ @Valid註解,會導致「java.lang.IllegalStateException:Validator的無效目標」exeption。 –

+0

改爲使用\ @InitBinder(「attrName」),或者在特定控制器中執行initBinder。 –

0

,其實這可以使用Spring驗證和JSR303來實現。

  • 公開一個MethodValidationPostProcessor bean。
  • 使用@Validated(org.springframework.validation.annotation)註釋您的控制器類。已驗證)
  • 在您的MyEntity字段/方法上使用JSR303驗證註釋。
  • 用@Valid註釋你的RequestBody參數(你已經在你的例子中完成了這個)。
  • 添加@ExceptionHandler方法來處理MethodArgumentNotValidException。這可以在控制器或@ControllerAdvice類中完成。