30

I am teaching myself Spring by dissecting example applications and then adding code here and there to test theories that I develop during the dissection. I am getting the following error message when testing some code that I added to a Spring application:

An Errors/BindingResult argument is expected to be declared immediately after the  
model attribute, the @RequestBody or the @RequestPart arguments to which they apply  

The method to which the error message refers is:

@RequestMapping(value = "/catowners", method = RequestMethod.GET)
public String findOwnersOfPetType(Integer typeID, BindingResult result, Map<String, Object> model) {
    // find owners of a specific type of pet
    typeID = 1;//this is just a placeholder
    Collection<Owner> results = this.clinicService.findOwnerByPetType(typeID);
    model.put("selections", results);
    return "owners/catowners";
}  

This error message was triggered when I tried to load the /catowners url pattern in the web browser. I have reviewed this page and this posting, but the explanation does not seem clear.

Can anyone show me how to fix this error, and also explain what it means?

EDIT:
Based on Biju Kunjummen's response, I changed the syntax to the following:

@RequestMapping(value = "/catowners", method = RequestMethod.GET)
public String findOwnersOfPetType(@Valid Integer typeID, BindingResult result, Map<String, Object> model)  

I am still getting the same error message. Is ther something I am not understanding?

SECOND EDIT:

Based on Sotirios's comment, I changed the code to the following:

@RequestMapping(value = "/catowners", method = RequestMethod.GET)
public String findOwnersOfPetType(BindingResult result, Map<String, Object> model) {
    // find owners of a specific type of pet
    Integer typeID = 1;//this is just a placeholder
    Collection<Owner> results = this.clinicService.findOwnerByPetType(typeID);
    model.put("selections", results);
    return "owners/catowners";
 }

I am still getting the same error message after telling eclipse to run as...run on server again.

Is there something that I am not understanding?

Community
  • 1
  • 1
CodeMed
  • 10,926
  • 68
  • 193
  • 335

2 Answers2

23

Spring uses an interface called HandlerMethodArgumentResolver to resolve the parameter in your handler methods and construct an object to pass as an argument.

If it doesn't find one, it passes null (I have to verify this).

The BindingResult is a result object that holds errors that may have come up validating a @ModelAttribute, @Valid, @RequestBody or @RequestPart, so you can only use it with parameters that are annotated as such. There are HandlerMethodArgumentResolver for each of those annotations.

EDIT (response to comment)

Your example seems to show that the user should provide a pet type (as an integer). I would change the method to

@RequestMapping(value = "/catowners", method = RequestMethod.GET)
public String findOwnersOfPetType(@RequestParam("type") Integer typeID, Map<String, Object> model)

And you would make your request (depending on your config) as

localhost:8080/yourcontext/catowners?type=1

Here too there is nothing to validate so you don't want or need a BindingResult. It would fail if you tried to add it anyway.

Sotirios Delimanolis
  • 263,859
  • 56
  • 671
  • 702
  • 1
    Lol, don't edit my question. There are more rules to the `HandlerMethodArgumentResolver`. Where do you expect the `Integer` object to come from? A `@ModelAttribute` is something that is build from the `HttpServletRequest` object, so are the others. – Sotirios Delimanolis Sep 05 '13 at 21:44
  • I was trying to make the edit that I subsequently just made to my own original posting above. Stack overflow would not allow me to delete the accidental edit to your posting. Can you suggest any actual code to fix the problem? These answers seem esoteric, and the actual code that I put in my edit above does not remove the error. – CodeMed Sep 05 '13 at 21:46
  • 1
    @CodeMed Where is the `Integer` coming from? Is it something you want from the url path, some request parameter, etc.? – Sotirios Delimanolis Sep 05 '13 at 21:48
  • This method is in the OwnerController class of the Spring PetClinic application. I added this method as part of some changes to create a page that returns all owners of a given type of pet. Subsequently, I would like to not have to create a separate method for each pet type. However, for starters, I was just trying to output a page with the cat owners in an output table. And this is the method that is throwing an error. The integer is the type_id=1 for cats in the petclinic application's database. – CodeMed Sep 05 '13 at 21:52
  • @CodeMed From what I understand, you want users to provide a type. My edit shows how. – Sotirios Delimanolis Sep 05 '13 at 21:56
  • Thank you for that. Actually, though, I do not want users to submit a type. Instead, I want users to click a link to the .../catowners url and have these results come up. Or click a link to the .../dogowners url and have a table of dog owners come up. I am using the numbers simply because the structure of the database causes the sql code to be simpler if I use type id number instead of type name. – CodeMed Sep 05 '13 at 21:59
  • @CodeMed Spring doesn't know how to provide an `Integer` argument to your handler method. You don't need it in your example since you are reassigning the reference anyway. Just declare and initialize the `Integer` yourself in the method body. – Sotirios Delimanolis Sep 05 '13 at 22:02
  • So do you mean that I need a separate method for each result set? One for cat owners, another for dog owners, another for lizard owners, another for hamster owners, another for bird owners, etc.? – CodeMed Sep 05 '13 at 22:04
  • @CodeMed No, that is irrelevant here. The issue here is understanding how Spring uses the methods you annotate with `@RequestMapping`. It uses the `HttpServletRequest` and a bunch of conventions to decide which arguments it can create and provide for your method. It then uses reflection to instantiate some of those, perform some logic, and provide them, again through reflection, by invoking your method. In your example, you haven't told it where it should be getting the `Integer` parameter from, so it will just assume some default behavior. – Sotirios Delimanolis Sep 05 '13 at 22:11
  • I just added a second edit to my original posting above. I tried code which I thought included your suggestions, but I got the same error message. Any further suggestions? Do I need to dig into the clinicservice object? The error message indicates that the ownercontroller object is the culprit. – CodeMed Sep 05 '13 at 22:12
  • @codemed I commented on your edit. I gtg now, but I'll be one later, we can chat. – Sotirios Delimanolis Sep 05 '13 at 22:14
  • Thanks. It works now, so I accepted this as the answer. I am on to a subsequent problem at this link. Do you have any suggestions? – CodeMed Sep 06 '13 at 00:17
7

If you have a parameter of type BindingResult it is essentially to hold any errors when binding http request parameters to a variable declared directly preceding the BindingResult method parameter.

So all of these is acceptable:

@RequestMapping(value = "/catowners", method = RequestMethod.GET)
public String findOwnersOfPetType(@Valid MyType type, BindingResult result, ...)


@RequestMapping(value = "/catowners", method = RequestMethod.GET)
public String findOwnersOfPetType(@ModelAttribute MyType type, BindingResult result, ...)

@RequestMapping(value = "/catowners", method = RequestMethod.GET)
public String findOwnersOfPetType(@RequestBody @Valid MyType type, BindingResult result, ...)
Biju Kunjummen
  • 47,594
  • 14
  • 110
  • 123
  • Thank you for taking the time to write a thoughtful answer. I made an edit to my original posting above using what I thought you were suggesting, but it is still throwing an error message. Is there something I am not understanding? – CodeMed Sep 05 '13 at 21:48
  • 3
    `@Valid` with a integer field will not work. Try to use a containing type instead with a integer field within it followed by the `BindingResult` parameter. – Biju Kunjummen Sep 05 '13 at 22:34
  • +1 and thank you. I have now fixed the problem using the various comments on this page. – CodeMed Sep 05 '13 at 22:36