带有 BindingResult 的 @Valid JSON 请求导致 IllegalStateException
Posted
技术标签:
【中文标题】带有 BindingResult 的 @Valid JSON 请求导致 IllegalStateException【英文标题】:@Valid JSON request with BindingResult causes IllegalStateException 【发布时间】:2012-02-18 16:31:26 【问题描述】:我有一个接受 JSON 请求的 REST 服务。我想验证传入的 JSON 请求值。我该怎么做?
在 Spring 3.1.0 RELEASE 中,我知道有人想确保他们使用3.1.13 New HandlerMethod-based Support Classes For Annotated Controller Processing 列出的最新支持类
旧的项目是:AnnotationMethodHandlerAdapter
。我想确保我使用的是最新版本,例如 RequestMappingHandlerAdapter
。
这是因为我希望它能解决我看到的问题:
java.lang.IllegalStateException:在没有前面的模型属性的情况下声明了错误/BindingResult 参数。检查您的处理程序方法签名!
我的@Controller
处理方法和相关代码是这样的:
@Autowired FooValidator fooValidator;
@RequestMapping(value="/somepath/foo", method=RequestMethod.POST)
public @ResponseBody Map<String, String> fooBar(
@Valid @RequestBody Map<String, String> specificRequest,
BindingResult results)
out("fooBar called");
// get vin from JSON (reportRequest)
return null;
@InitBinder("specificRequest") // possible to leave off for global behavior
protected void initBinder(WebDataBinder binder)
binder.setValidator(fooValidator);
FooValidator
看起来像这样:
@Component
public class FooValidator implements Validator
public boolean supports(Class<?> clazz)
out("supports called ");
return Map.class.equals(clazz);
public void validate(Object target, Errors errors)
out("validate called ");
private void out(String msg)
System.out.println("****** " + getClass().getName() + ": " + msg);
如果我删除 BindingResult
,一切正常,但我无法判断 JSON 是否经过验证。
对于 JSON 请求使用 Map<String, String>
或使用单独的验证器而不是带有验证注释的自定义 Bean(您如何为 JSON 请求执行此操作?)的概念,我并不强烈依赖。任何可以验证 JSON 请求的东西。
【问题讨论】:
我们认为,如果我们能够以某种方式确保我们使用的是较新的类,例如 RequestMappingHandlerAdapter,那么这可能会起作用。你是如何设置的,或者这个问题还有其他好的解决方案吗? 【参考方案1】:3.1.17 @Valid On @RequestBody Controller Method Arguments 说:
@RequestBody
方法参数可以使用@Valid
进行注释,以调用类似于对@ModelAttribute
方法参数的支持的自动验证。生成的MethodArgumentNotValidException
在DefaultHandlerExceptionResolver
中处理并生成400
响应代码。
换句话说,如果您使用@Valid @RequestBody
,那么Spring 将在调用您的方法之前拒绝无效请求。如果你的方法被调用,那么你可以假设请求体是有效的。
BindingResult
用于验证表单/命令对象,而不是@RequestBody
。
【讨论】:
我期待着尝试一下!谢谢! 我们尝试删除 BindingResult,但验证无效。 我们正在使用 JSR303,我在模型类中遇到了空值问题。我想将错误发送回视图,但在 DefaultHandler 中处理它们会触发 MethodArgumentNotValidException。我认为到那时我无法优雅地处理这件事为时已晚。 这里是MethodArgumentNotValidException
处理的样本:dzone.com/articles/spring-31-valid-requestbody【参考方案2】:
我不得不做一次类似的事情。我刚刚创建了一个可以将 JSON 转换为的 Java 对象并使用GSON
进行转换,从而使我的生活变得更简单。
其实很简单:
@Autowired
private Gson gson;
@RequestMapping(value = "/path/info", method = RequestMethod.POST)
public String myMethod(@RequestParam(value = "data") String data,
Model model,
@Valid MyCustomObject myObj,
BindingResult result)
//myObj does not contain any validation information.
//we are just using it as as bean to take advantage of the spring mvc framework.
//data contains the json string.
myObj = gson.fromJson(data, MyCustomObject.class);
//validate the object any way you want.
//Simplest approach would be to create your own custom validator
//to do this in Spring or even simpler would be just to do it manually here.
new MyCustomObjValidator().validate(myObj, result);
if (result.hasErrors())
return myErrorView;
return mySuccessView;
在您的自定义 Validator
类中进行所有验证:
public class MyCustomObjValidator implements Validator
@Override
public boolean supports(Class<?> clazz)
return MyCustomObj.class.equals(clazz);
@Override
public void validate(Object target, Errors errors)
MyCustomObj c = (MyCustomObj) target;
Date startDate = c.getStartDate();
Date endDate = c.getEndDate();
if (startDate == null)
errors.rejectValue("startDate", "validation.required");
if (endDate == null)
errors.rejectValue("endDate", "validation.required");
if(startDate != null && endDate != null && endDate.before(startDate))
errors.rejectValue("endDate", "validation.notbefore.startdate");
MyCustomObject
不包含任何用于验证的注释,这是因为否则 Spring 将尝试验证此对象中当前为空的字段,因为所有数据都在 JSON 字符串中,例如:
public class MyCustomObject implements Serializable
private Date startDate;
private Date endDate;
public Date getStartDate()
return startDate;
public Date getEndDate()
return endDate;
public void setStartDate(Date theDate)
this.startDate = theDate;
public void setEndDate(Date theDate)
this.endDate = theDate;
【讨论】:
您的帖子中的@RequestParam(value = "data") 是什么?我试图做这样的想法,但我有一个例外,它是必需的属性。 解决方案显式调用Validator,这应该由Spring框架完成。【参考方案3】:尝试使用以下方法:
@Autowired
private FooValidator fooValidator;
@InitBinder("specificRequest") // possible to leave off for global behavior
protected void initBinder(WebDataBinder binder)
binder.setValidator(fooValidator);
@ModelAttribute("specificRequest")
public Map<String, String> getModel()
return new HashMap<String, String>();
这将使您的控制器将请求序列化为您指定的类型。 我不得不说我通常不会为验证器提供服务(自动装配),但它可能会更好。
您的处理程序现在看起来像这样:
@RequestMapping(value="/somepath/foo", method=RequestMethod.POST)
public @ResponseBody Map<String, String> fooBar(
@Valid @ModelAttribute("specificRequest")
Map<String, String> specificRequest, BindingResult results)
out("fooBar called");
// get vin from JSON (reportRequest)
return null;
据我所知,这可以完美地解决您收到的错误。
【讨论】:
以上是关于带有 BindingResult 的 @Valid JSON 请求导致 IllegalStateException的主要内容,如果未能解决你的问题,请参考以下文章
Java使用@Valid+BindingResult进行controller参数校验
使用@Valid+BindingResult进行controller参数校验
在 Spring Boot 应用程序中使用 @Valid 和 BindingResult 时的表单输入验证问题