2013-06-20 137 views
18

在Spring MVC的REST服務(JSON)豆類的名單,我有一個控制器方法,像這樣的:Spring MVC的 - @Valid在REST服務

@RequestMapping(method = RequestMethod.POST, value = { "/doesntmatter" }) 
@ResponseBody 
public List<...> myMethod(@Valid @RequestBody List<MyBean> request, BindingResult bindingResult) { 

凡爲myBean類有Bean驗證註解。

在這種情況下驗證似乎不會發生,雖然它適用於其他控制器。

我不想在dto中封裝列表,這會改變json輸入。

爲什麼沒有對bean列表進行驗證?有什麼選擇?


+0

您可以使用此https://stackoverflow.com/a/36313615/ 3110023 – mehdi

回答

21

@Valid是JSR-303註解和JSR-303適用於驗證了JavaBeans。 A java.util.List不是JavaBean(根據JavaBean的official description),因此無法使用符合JSR-303的驗證器直接進行驗證。這得到兩個觀察結果的支持。

Section 3.1。JSR-303 Specification的3表示:

除了支持實例驗證之外,還支持對象圖的驗證。圖形驗證的結果作爲一組統一的約束違規返回。考慮這樣的情況,其中bean X包含Y型的域。通過使用@Valid註釋註釋字段Y,驗證者將在驗證X時驗證Y(及其屬性)。在類型Y(子類,實現)中聲明的字段中包含的值的確切類型Z在運行時確定。使用Z的約束定義。這確保了標記爲@Valid的關聯的正確多態行爲。

集合值,數組值和通常可迭代字段和屬性也可與@Valid註釋進行裝飾。這會導致迭代器的內容得到驗證。任何實現java.lang.Iterable的對象都被支持。

我已經用粗體標出了重要的信息。本節意味着爲了使集合類型得到驗證,它必須封裝在一個bean中(暗示爲Consider the situation where bean X contains a field of type Y);此外,收集不能直接進行驗證(暗示爲Collection-valued, array-valued and generally Iterable fields and properties may also be decorated,重點在字段和屬性)。

實際JSR-303實現

我有a sample application,測試驗證採集與Hibernate驗證和Apache豆類驗證兩者。如果對此示例運行mvn clean test -Phibernate(使用Hibernate Validator)和mvn clean test -Papache(對於Beans Validator),則兩者都會拒絕直接驗證集合,這似乎符合規範。由於Hibernate Validator是JSR-303的參考實現,因此此示例進一步證明了集合需要封裝在bean中以便進行驗證。


隨着那清,我要說的是,也有在試圖收集直接在問題中的顯示方式傳遞給控制器​​的方法的設計問題。即使驗證直接在集合上工作,控制器方法也將無法處理替代數據表示,如定製XML,SOAP,ATOM,EDI,Google Protocol Buffers等,這些表示不直接映射到集合。爲了支持這些表示,控制器必須接受並返回對象實例。這需要以任何方式將集合封裝在對象實例中。因此,建議在其他對象中包含List,這是非常可取的。

2

實現自己的驗證與org.springframework.validation.beanvalidation.LocalValidatorFactoryBean爲成員,並調用驗證每個項目。

public class CheckOutValidator implements Validator { 


    private Validator validator; 

    @Override 
    public void validate(Object target, Errors errors) { 
    List request = (List) target; 
    Iterator it = request.iterator() 
    while(it.hasNext()) { 
    MyBean b = it.next(); 
    validator.validate(b, errors); 

    } 

    } 

//setters and getters 

} 
8

我可以找到這樣做的唯一方法是包裝清單,這也意味着JSON輸入將不得不改變

@RequestMapping(method = RequestMethod.POST, value = { "/doesntmatter" }) 
@ResponseBody 
public List<...> myMethod(@Valid @RequestBody List<MyBean> request, BindingResult bindingResult) { 

變爲:

@RequestMapping(method = RequestMethod.POST, value = { "/doesntmatter" }) 
@ResponseBody 
public List<...> myMethod(@Valid @RequestBody MyBeanList request, BindingResult bindingResult) { 

,我們還需要:

import javax.validation.Valid; 
import java.util.List; 

public class MyBeanList { 

    @Valid 
    List<MyBean> list; 

    //getters and setters.... 
} 

這看起來也是可能與列出的自定義validatior,但我沒有得到那麼遠, 。

@Valid註釋是標準JSR-303 Bean驗證API的一部分,並且不是特定於Spring的構造。 Spring MVC將在綁定後驗證@Valid對象,因爲已經配置了適當的Validator。

參考:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html

2

嘗試直接驗證。事情是這樣的:

@Autowired 
Validator validator; 

@RequestMapping(method = RequestMethod.POST, value = { "/doesntmatter" }) 
@ResponseBody 
public Object myMethod(@RequestBody List<Object> request, BindingResult bindingResult) { 
    for (int i = 0; i < request.size(); i++) { 
     Object o = request.get(i); 
     BeanPropertyBindingResult errors = new BeanPropertyBindingResult(o, String.format("o[%d]", i)); 
     validator.validate(o, errors); 
     if (errors.hasErrors()) 
      bindingResult.addAllErrors(errors); 
    } 
    if (bindingResult.hasErrors()) 
     ... 
1

有一個優雅的方式來包裝您的請求在自定義java.util.List作爲ListJavaBean作爲。 see here

0

如果你不想爲你寫有每個列表的包裝,你可以使用一個通用的包裝:

public class ListWrapper<E> { 

    private List<E> list; 

    public ListWrapper() { 
     list = new ArrayList<>(); 
    } 

    public ListWrapper(List<E> list) { 
     this.list = list; 
    } 

    @Valid 
    public List<E> getList() { 
     return list; 
    } 

    public void setList(List<E> list) { 
     this.list = list; 
    } 

    public boolean add(E e) { 
     return list.add(e); 
    } 

    public void clear() { 
     list.clear(); 
    } 

}