59

Having the following code:

@RequestMapping(value = "/greeting", method = POST, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
@ResponseBody
public String greetingJson(@RequestBody String json) {
    System.out.println("json = " + json); // TODO json is null... how to retrieve plain json body?
    return "Hello World!";
}

The String json argument is always null despite json being sent in the body.

Note that I don't want automatic type conversion, I just want the plain json result.

This for example works:

@RequestMapping(value = "/greeting", method = POST, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
@ResponseBody
public String greetingJson(@RequestBody User user) {
    return String.format("Hello %s!", user);
}

Probably I can use the use the ServletRequest or InputStream as argument to retrieve the actual body, but I wonder if there is an easier way?

Marcel Overdijk
  • 9,358
  • 15
  • 59
  • 100
  • 1
    `String jsonBody = IOUtils.toString( request.getInputStream());` one-liner with Apache if you need it done quickly. [And similar question.](http://stackoverflow.com/questions/5806724/springs-requestbody-providing-empty-string-on-post) – madhead Jul 25 '13 at 19:33
  • This will probably indeed work, however I would like to get rid of having http request as arguments in action, and let Spring automatically get the content in a var... (thanks for your reply anyway) – Marcel Overdijk Jul 26 '13 at 05:28

5 Answers5

89

Best way I found until now is:

@RequestMapping(value = "/greeting", method = POST, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
@ResponseBody
public String greetingJson(HttpEntity<String> httpEntity) {
    String json = httpEntity.getBody();
    // json contains the plain json string

Let me know if there are other alternatives.

Marcel Overdijk
  • 9,358
  • 15
  • 59
  • 100
  • 6
    I had to add `@RequestBody` before `HttpEntity httpEntity` to make this work. Hope this helps. – bappak Jun 03 '19 at 20:13
  • 1
    Neither way worked for me, sadly, with my custom `HttpMessageConverter`-s. – Klesun Mar 03 '20 at 17:35
  • I had the same issue like you, but I now solved it. Instead of overriding the configureMessageConverters, override the: extendMessageConverters Now you can use @RequestBody String body – Yuval Barness Apr 16 '20 at 20:52
  • you can directly annotate with @RequestBody ex - public ResponseEntity request(@RequestBody String json) {...} – Vivek Swansi May 28 '20 at 13:52
20

You can just use

@RequestBody String pBody

aGO
  • 1,045
  • 8
  • 25
  • 2
    This doesn't work for me. I've receiving a URL-encoded form data string containing both the URL query parameters and the POST data, not a raw JSON copy of the data as required. – Jules Oct 04 '17 at 08:11
  • Are you using application/json as content-type?? – aGO Oct 04 '17 at 14:57
  • Yes. Checked the request via chrome's inspector and all the headers look good, including content-type. But Spring is URL-encoding it, appending it to the URL-encoded query string, and giving me that. @davidcassidy's approach above, however, works fine. – Jules Oct 04 '17 at 17:21
18

Only HttpServletRequest worked for me. HttpEntity gave null string.

import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;

@RequestMapping(value = "/greeting", method = POST, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
@ResponseBody
public String greetingJson(HttpServletRequest request) throws IOException {
    final String json = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
    System.out.println("json = " + json);
    return "Hello World!";
}
Klesun
  • 7,746
  • 5
  • 37
  • 41
Jarno Argillander
  • 5,645
  • 2
  • 29
  • 32
7

simplest way that works for me is

@RequestMapping(value = "/greeting", method = POST, consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public String greetingJson(String raw) {
    System.out.println("json = " + raw);
    return "OK";
}
david cassidy
  • 79
  • 1
  • 1
2

If you have dozens of Methods that need to get HTTP body as JSON and convert it to custom data type, it is a better way to implement the support on the framework

public static class Data {
    private String foo;
    private String bar;
}

//convert http body to Data object.
//you can also use String parameter type to get the raw json text.
@RequestMapping(value = "/greeting")
@ResponseBody
public String greetingJson(@JsonBody Data data) {
    System.out.println(data);
    return "OK";
}

notice that we using user defined annotation @JsonBody.

// define custom annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface JsonBody {
    String encoding() default "utf-8";
}

//annotation processor for JsonBody 
@Slf4j
public class JsonBodyArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterAnnotation(JsonBody.class) != null;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
                              WebDataBinderFactory binderFactory) throws Exception {
        JsonBody annotation = parameter.getParameterAnnotation(JsonBody.class);
        assert annotation != null;
        ServletRequest servletRequest = webRequest.getNativeRequest(ServletRequest.class);
        if (servletRequest == null) {
            throw new Exception("can not get ServletRequest from NativeWebRequest");
        }
        String copy = StreamUtils.copyToString(servletRequest.getInputStream(), Charset.forName(annotation.encoding()));
        return new Gson().fromJson(copy, parameter.getGenericParameterType());
    }
}

// register the annotation processor
@Component
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new JsonBodyArgumentResolver());
    }
}
Joseph
  • 49
  • 4
  • it's too much complicated, just need to receive it by HttpServletRequest, HttpEntity or pure String with @RequestBody annotation – Oleksandr Loushkin May 17 '20 at 15:38
  • @OleksandrLoushkin Yes,It is more complex in implementation, but simpler in use. If you use HttpServletRequest / HttpEntity, you need to manually get string from InputStream, and then convert it from json to java Object. A good tool should be simpler to use. – Joseph May 18 '20 at 02:36