3

Before I ask my question I have to say that I have read more than 20 questions and articles about this problem and none of them could solve it.

My problem is I have a restful server in java like this:

@RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
@ResponseBody
public void downloadByCode(@RequestBody final String stringRequest, final HttpServletResponse response)
{
    try
    {
        final ObjectMapper objectMapper = new ObjectMapper();
        final JsonNode jsonRequest = objectMapper.readValue(stringRequest, JsonNode.class);

        // ...
        // some processings here to create the result
        // ....

        final ServletOutputStream outputStream = response.getOutputStream();
        outputStream.write(result);
        // Flush the result
        outputStream.flush();
    }
    catch (final Exception exception)
    {
        LOG.debug("Exception Thrown [downloadByCode]", exception);
    }
}

And I have tried different ways to send a json to this server with jquery (but all of them create errors):

$.ajax({
   url:"/downloadByCode",
   type:"POST",
   data: JSON.stringify({"name":"value"}) });

415 "errors message" : "Content type 'application/x-www-form urlencoded;charset=UTF-8' not supported", "type" : "HttpMediaTypeNotSupportedError"

So I tried to fix it by adding contentType:

$.ajax({
   url:"/downloadByCode",
   contentType:"application/json",
   type:"POST",
   data: JSON.stringify({"name":"value"}) });

400 "errors message" : "Could not instantiate JAXBContext for class [class java.lang.String]: null; nested exception is javax.xml.bind.JAXBException\n - with linked exception:\n[java.lang.NullPointerException", "type" :"HttpMessageConversionError"

I tried to send json object directly instead of JSON.stringify and it gives the same 400 error.

I tried to add different consumes to the @requestMapping but still no luck.

I tried to define my own class instead of JsonNode but it does not change anything.

Any ideas?

12
  • Have you tried changing your @RequestBody parameter type to Map<String, Object> or creating a class with a single "name" property and using that as the type (as suggested by Vinh Vo)? Commented Jan 11, 2016 at 16:55
  • @NTyler, I never tried Map<String, Object>, I am going to try it now. Thank you for the suggestion Commented Jan 11, 2016 at 17:40
  • Vinh Vo method creates the following error: Could not instantiate JAXBContext for class [$InputData]: \r\n Exception Description: $InputData requires a zero argument constructor or a specified factory method. Note that non-static inner classes do not have zero argument constructors and are not supported. Commented Jan 11, 2016 at 17:40
  • 2
    You probably need to use a POJO class then as suggested by Vinh Vo. Declare the class static or put it in a separate file and you should be all set. Commented Jan 11, 2016 at 19:18
  • 1
    Bounty should go to Vinh Vo, his answer was correct and first. The class only needs to be static because you made it an inner class, if you create the class in its own file, the static modifier is unnecessary. Commented Jan 11, 2016 at 21:30

11 Answers 11

3
+50

Please try to create new class :

public class InputData{
   private String name;
   public String getName(){
      return name;
   }
   public void setName(String name){
      this.name = name;
   }
}

Then

public void downloadByCode(@RequestBody InputData stringRequest, final HttpServletResponse response)

And

$.ajax({
   url:"/downloadByCode",
   contentType:"application/json",
   type:"POST",
   data: JSON.stringify({"name":"value"}) });
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for the suggestion but I am getting this error: Could not instantiate JAXBContext for class [$InputData]: \r\n Exception Description: $InputData requires a zero argument constructor or a specified factory method. Note that non-static inner classes do not have zero argument constructors and are not supported.
1

try @RequestBody final Map<String, String> stringRequest

also you will need consumes = "application/json" on the @RequestMapping because you have that in your AJAX call

You will get 400 if spring doesn't like the format in which you send your ajax - I've had so much trouble with this in the past and it seems better to just ignore header types and content types unless necessary

2 Comments

Thank you for the answer. I have changed my request body to this: public void downloadOrdersByCode(@RequestBody final Map<String, String> requestMap, final HttpServletResponse response) But, I am still getting same 400 error as above
You also need the consumes part of my answer (or just take it out of your ajax call) - does that work?
1

You might try sending your response back as a ResponseEntity instead of using the HttpServletResponse directly. My hunch is that second argument, the HttpServletRequest argument, is what is causing the problem. I've never used that. I've always send my response back using the spring mvc api.

1 Comment

Thank you for the response, the problem is it never arrives to the response phase. I mean it generates error when it tries to receive the json as input. My response is a CVS file and I think the current code should be fine because it works perfectly with GET method.
1

With Jersey api you can try just: @POST public void downloadByCode(String stringRequest)

and I think you'll find the body of your post in stringRequest.

1 Comment

Thanks, maybe I give it a shot if I could not solve it without an additional API.
1

You can take request body as string with usage of org.springframework.http.HttpEntity<String> as request type, here is example with your code as base:

@RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
@ResponseBody
public void downloadByCode(final HttpEntity<String> request, final HttpServletResponse response)
{
    try
    {
        final ObjectMapper objectMapper = new ObjectMapper();
        final JsonNode jsonRequest = objectMapper.readValue(request.getBody(), JsonNode.class);

        // ...
        // some processings here to create the result
        // ....

        final ServletOutputStream outputStream = response.getOutputStream();
        outputStream.write(result);
        // Flush the result
        outputStream.flush();
    }
    catch (final Exception exception)
    {
        LOG.debug("Exception Thrown [downloadByCode]", exception);
    }
}

But maybe it will be better to use also String as return type, if you are planning to return result as string value, like this:

@RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
@ResponseBody
public String downloadByCode(HttpEntity<String> request) {
    String requestBody = request.getBody();
    String result;

    // ...
    // some processings here to create the result text
    // ....

    return result;
}

I made simple application using Spring Boot with usage of proposed solutions using HttpEntity and also additional example of usage POJO, to run application you need to have Maven and JDK >= 1.7.

#clonning repository with sample
git clone [email protected]:mind-blowing/samples.git

#change current folder to sample application root
cd samples/spring-boot/rest-handler-for-plain-text

#running application using maven spring-boot plugin
mvn spring-boot:run

After application will be started you can open http://localhost:8080 and you will see html page with simple usage of JQuery to send POST requests, text of request and response will visible on html page, in controller I added two handlers, first with usage of HttpEntity and second with usage of POJO.

Controller: SampleRestController.java

HTML page: index.html

Project: https://github.com/mind-blowing/samples/tree/master/spring-boot/rest-handler-for-plain-text

7 Comments

I am going to give it a try! Thanks :)
I get this error: Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported
I use this jquery ajax: $.ajax({ url:"/downloadByCode", type:"POST", data: JSON.stringify({"name":"value"}) });
Try to use in request from js option contentType: "application/json" as you did previously. $.ajax({ url: "/downloadByCode", contentType: "application/json", type: "POST", data: '{"name":"value"}' }).always(function (data) { alert(data); });
I tested it with "application/json" contentType and I get this error: Could not instantiate JAXBContext for class [class java.lang.String]: null; nested exception is javax.xml.bind.JAXBException\n - with linked exception:\n[java.lang.NullPointerException]
|
1

First of all If you are using maven you should add dependency for jackson

 <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.4.1</version>
    </dependency>

or you can download the jar and put it in our project class path (you can use other mapper as well)

then you should create a model or DTO class where you can map your json

 public class Data{
     private String name;

     pubilc Data(){}

    //getter and setter

 }

THEN you controller

@RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
@ResponseBody
public Data downloadByCode(@RequestBody final Data data, final HttpServletResponse response)
{
//your code
    return data;
}

AJAX CALL

$.ajax({
   url:"/downloadByCode",
   contentType:"application/json",
   type:"POST",
   data: JSON.stringify({"name":"value"}) });

(Optional)You can override behavior by telling object mapper not to fail on missing properties by defining the bean as follows:

 @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false));
        return converter;
    }

http://websystique.com/springmvc/spring-mvc-requestbody-responsebody-example/

1 Comment

Thank you for your help. This answer is how everything should work "ideally"! but, it generates error in my environment.
1

Looking at your errors, it's clear that you have configured 'Jaxb2RootElementHttpMessageConverter' or similar XML converter in your spring configuration. And since you have registerned an XML converter, the @RequestBody and @ResponseBody work based on the registered message converters.

So, to solve your problem, go with a JSON message converter such as 'MappingJacksonHttpMessageConverter'. Once you register a JSON message converter, create a bean class to hold your json data and use it with RequestBody as below:

// It has to meet the json structure you are mapping it with
public class YourInputData {
    //properties with getters and setters
}

Update 1:

Since you have defined multiple message converters, Spring tries to use the first one available by default. In order to use specific message converter(in this case Jackson converter), you should specify 'Accept' header from client like below:

$.ajax({     
          headers: {          
                 "Accept" : "application/json; charset=utf-8",         
                "Content-Type": "application/json; charset=utf-8"   
  }     
  data: "data",    
  success : function(response) {  
       ...  
   } })

5 Comments

Thank you for the response, I already have json converter: <bean class="org.springframework.http.converter.json.MappingJacksonHttp essageConverter" />
And I created the bean class with same structure as my json {"name","value"} but I get this error: Could not instantiate JAXBContext for class [$InputData]: \r\n Exception Description: $InputData requires a zero argument constructor or a specified factory method. Note that non-static inner classes do not have zero argument constructors and are not supported
The actual issue is it tries to use JAXB converter when it's supposed to use Jackson converter.
I think you are absolutely right. I defined three message converters <bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>, <bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/>, and <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" /> . Now the question is why it is using JAXB instead of jackson while my contentType is application/json.
I noticed that my json structure class was an inline class and it should be static. After adding static to my class now spring choose the correct converter and the accept tag is unnecessary. I appreciate your effort to solve this issue.
1

The final answer is a combination of a number of answers/comments in this question that I am going to summarize them here:

1- You have to make sure you have an appropriate json converter in your spring config such as MappingJacksonHttpMessageConverter (credits to @java Anto)

2- You have to create a POJO class with same structure as your json object (see @Vinh Vo answer)

3- Your POJO class cannot be an inline class unless it is a static class. It means it should have its own java file or it should be static. (credits to @NTyler)

4- Your POJO class can miss parts of your json object if you set it appropriately in your object mapper (see @Aman Tuladhar answer)

5- Your ajax call requires contentType:"application/json", and you should send your data with JSON.stringify

Here is the Final code that is working perfectly:

public static class InputData
{
    private String name

    public String getName()
    {
        return name;
    }

    public void setName(final String name
    {
        this.name = name;
    }
}

@RequestMapping(value = "/downloadByCode", method = RequestMethod.POST)

@ResponseBody
public void downloadByCode(@RequestBody final InputData request, final HttpServletResponse response)
{
    try
    {
        String codes = request.getName();

        // ...
        // some processings here to create the result
        // ....

        final ServletOutputStream outputStream = response.getOutputStream();
        outputStream.write(result);
        // Flush the result
        outputStream.flush();
    }
    catch (final Exception exception)
    {
        LOG.debug("Exception Thrown [downloadByCode]", exception);
    }
}

And it is the jquery Ajax request:

$.ajax({
   url:"/downloadByCode",
   contentType:"application/json",
   type:"POST",
   data: JSON.stringify({"name":"value"}) });

Comments

0

Delete the @ResponseBody on your downloadByCode method

1 Comment

Nothing changed, I think the problem is related to getting json as input. The output part looks fine.
0

Change your method downloadByCode() return type to String and then return the String

Response body will automatically convert the returned String to JSON and then use the data appropriately

1 Comment

It never arrives to response phase. It generates error when it tries to load json as input.
0

I am not that well versed with java but as much as I know your java code must be something like this.

public class downloadByCode{
  @GET
  @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
  public Response downloadByCode(@QueryParam("paramater1") final String parameter 1, @Context HttpServletRequest httpRequest) {

If this not helps you can keep you code somewhere and share it.

1 Comment

I think this code is based on GET method, I want to get JSON object (or JSON string) with POST method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.