java.lang.IllegalStateException: Invalid target for Validator [idv.neil.model.ProductValidator@5ddffa]: idv.neil.web.Pager@11ba4a2 at org.springframework.validation.DataBinder.setValidator(DataBinder.java:498) at idv.neil.web.ctrl.ProductController.formBackingObject(ProductController.java:100) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at com.google.appengine.tools.development.agent.runtime.Runtime.invoke(Runtime.java:104)Spring MVC 3 以後可以使用 Annotation 將 MultiActionController 與 SimpleFormController 做在同一隻 controller:
@Controller @RequestMapping("/product") @SessionAttributes("cmd") public class ProductController { private static final Logger log = LoggerFactory.getLogger(ProductController.class); private ProductService productService; @Autowired public void setProductService(ProductService productService) { this.productService = productService; } @RequestMapping(method = RequestMethod.GET) public String form(@RequestParam(required = false) Long id, Model model) { Product cmd; if (id == null) { cmd = new Product(); } else { cmd = this.productService.getProduct(id); } model.addAttribute("cmd", cmd); return "productForm"; } @RequestMapping(method = RequestMethod.POST) public String sync(@Valid @ModelAttribute("cmd") Product cmd, BindingResult result) { if (result.hasErrors()) { return "productForm"; } if (cmd.getId() == null) { this.productService.addProduct(cmd); } else { this.productService.updateProduct(cmd); } return "redirect:/product/list.do"; } @RequestMapping("/list") public ModelAndView list(HttpServletRequest request) { ModelAndView mav = new ModelAndView("productList"); Pager pager = Pager.createPager(request, this.productService.countProduct()); List<Product> list = this.productService.listProduct( pager.getStart(), pager.getPageSize()); mav.addObject("list", list); mav.addObject(Pager.PAGER, pager); return mav; } @RequestMapping("/delete") public String delete(@RequestParam("id") Long id) { this.productService.deleteProduct(id); return "redirect:/product/list.do"; } @InitBinder public void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor( dateFormat, false)); binder.registerCustomEditor(Text.class, new TextPropertyEditor()); binder.setValidator(new ProductValidator()); } }在 @InitBinder 裡加入 binder.setValidator(new ProductValidator()) 之前,運作一切正常,加入後,在進入列表頁時就發生上述的錯誤。
最後發現問題出現在 @InitBinder 預設會在該 @Controller 所有 request 進來時執行,此時若設有 Validator,則會針對 Model 裡的所有物件進行驗證,但有一些例外,可以在 ModelFactory.isBindingCandidate(...) 看到例外。
為避免這種情形,@InitBinder 有一個 value 參數供設定,一旦設定後,就只有 command 或 form 的 attribute name 以及 request parameter 裡有符合的名稱時,才會執行該 @InitBinder,就以上的範例來看,應該改為 @InitBinder("cmd") 就沒問題了,除非在 form 以外的 request model 裡放了該死的 cmd 屬性。
沒有留言:
張貼留言