繁体   English   中英

带有BindingResult的@Valid JSON请求会导致IllegalStateException

[英]@Valid JSON request with BindingResult causes IllegalStateException

我有一个REST服务,它接受JSON请求。 我想验证即将发布的JSON请求值。我该怎么做?

在Spring 3.1.0 RELEASE中,我知道有人想确保他们使用3.1.13新的基于HandlerMethod的支持类列出的最新支持类, 用于带注释的控制器处理

旧的是像AnnotationMethodHandlerAdapter这样的项目。 我想确保我使用最新的RequestMappingHandlerAdapter

这是因为我希望它能解决我看到的问题:

java.lang.IllegalStateException:在没有先前模型属性的情况下声明的Errors / 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是否经过验证。

我并不强烈关注使用Map<String, String>作为JSON请求或使用单独的验证器而不是使用验证注释的自定义Bean的概念(如何为JSON请求执行此操作?)。 无论什么都可以验证JSON请求。

3.1.17 @Valid On @RequestBody控制器方法参数说:

可以使用@Valid@RequestBody方法参数进行批注,以调用类似于对@ModelAttribute方法参数的支持的自动验证。 DefaultHandlerExceptionResolver处理生成的MethodArgumentNotValidException ,并生成400响应代码。

换句话说,如果你使用@Valid @RequestBody那么Spring会在调用你的方法之前拒绝一个无效的请求。 如果调用你的方法,那么你可以假设请求体是有效的。

BindingResult用于验证表单/命令对象,而不是@RequestBody

我不得不做一次类似的事情。 我最终通过创建一个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;
    }
}

尝试使用以下内容:

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

据我所知,这很有效,可以解决您收到的错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM