3

I have a simple servlet as follows:

@RestController
public class TestServlet {
    @RequestMapping(value = "/test1")
    public String test1() {
        return "test1";
    }

    @RequestMapping(value = "/test2")
    public String test2(@RequestBody TestClass req) {
        return "test2";
    }

    public static class TestClass {
        private String value;

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }
}

But only the servlet not receiving parameters is working:

Works: http://localhost:8080/test1

Doesn't work: http://localhost:8080/test2?value=1234

org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public java.lang.String

Why is the @RequestBody annotation not working? Am I missing an important piece?

viruskimera
  • 193
  • 16
membersound
  • 81,582
  • 193
  • 585
  • 1,120
  • "Am I missing an important piece?" yup there's the request and it has a body (and the request body is not some url parameter), see http://stackoverflow.com/questions/28039709/what-is-difference-between-requestbody-and-requestparam and http://stackoverflow.com/questions/3337350/learning-springs-requestbody-and-requestparam?noredirect=1&lq=1 –  Dec 01 '16 at 12:21

4 Answers4

7

One of the differences between @Controller and @RestController is that you don't have to write @RequestBody and @ResponseBody, that means that any parameter in your controller method which does not have an annotation (like @PathVariable, @ModelAttribute, ...) will implicitly have @RequestBody, and must therefore be POSTed as the HTTP entity body. So you need to send JSON/XML as part of a POST. What you have done is to send data on as part of the URL, which makes it a request parameter and not body-data, and you need @RequestParam to to extract data from the URL.

Also, I would recommend that you use the @GetMapping/@PostMapping or include the method parameter in the @RequestMapping annotation, it is highly unlikely that you want a service to be used for both POST and GET, so you should be as specific as possible in you controller method descriptions, to limit error scenarios.

sbbs
  • 1,450
  • 2
  • 13
  • 20
Klaus Groenbaek
  • 4,820
  • 2
  • 15
  • 30
  • 1
    *Correction regarding **@RequestBody** annotation:* while `@RestController` is a _composed annotation_ that is itself _meta-annotated_ with `@Controller` and `@ResponseBody` to indicate a controller whose every method inherits the type-level `@ResponseBody` annotation, you would still need `@RequestBody` for a POST parameter. More details can be found in [this answer](https://stackoverflow.com/a/39343660/814702). – informatik01 Aug 27 '19 at 14:33
  • In my testcase it looks like you still need the `@RequestBody` if the argument is an Optional, even if you annotate the others with `@PathVariable`, etc.. and the controller as `@RestController` – Whimusical Nov 29 '19 at 20:13
3

The reason the second URL does not work is because when using @RequestBody the data you are sending to the endpoint needs to come through via the data attribute in the request header. When you append ?attr=value to your URL that is sending the attribute in the params header.

There are two ways to fix this:

  1. Change your endpoint to read something like this:

    public String test2(@RequestParam("value") TestClass req) { //Endpoint code }

  2. Change your endpoint to read something like this:

    @RequestMapping(value="test2",method=RequestMethod.POST) public String test2(@RequestBody TestClass req){ //Endpoint code }

    and make your call similar to this (e.g. angularjs):

    http.post({url:/*url*/,data:/*object to send*/});

The second option will most likely be what you want to go with because it looks like you are trying to send a json object to your endpoint and I believe you can only do that by making a POST request rather than a GET request

CraigR8806
  • 1,584
  • 13
  • 21
0

Just leave out the @RequestBody annotation, as this is only for POST requests.

  public String test2(@Valid TestClass req) {
        return "test2";
    }
membersound
  • 81,582
  • 193
  • 585
  • 1,120
  • This may work for your testing environment, but I don't believe that Jackson will auto deserialize the json for you if you wanted to use the object TestClass inside of your endpoint. The reason for this is because you cannot send json via a GET request – CraigR8806 Dec 01 '16 at 12:29
0

When you declare a controller method parameter as @RequestBody, you are wishing it to be recovered from the request body and not as a "regular" http parameter.

You could try using any kind of plugin for Firefox (RESTClient) or Chrome (PostMan) and try using one of them. You could do it using SoapUI as well.

The request should be a POST to the requested url this way:

POST http://localhost:8080/test2

You must provide http headers provinding expected Content-Type and Accept. In case of using Json, set them like this:

Content-Type: application/json
Accept: text/html (As your method returns only a String)

And then write the param to the request body. If in Json, like this:

{
"value":"the provided value"
}
jlumietu
  • 6,234
  • 3
  • 22
  • 31