Spring JSON Request Getting 406 (Not Acceptable)

Spring JSON request getting 406 (not Acceptable)

406 Not Acceptable

The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.

So, your request accept header is application/json and your controller is not able to return that. This happens when the correct HTTPMessageConverter can not be found to satisfy the @ResponseBody annotated return value. HTTPMessageConverter are automatically registered when you use the <mvc:annotation-driven>, given certain 3-d party libraries in the classpath.

Either you don't have the correct Jackson library in your classpath, or you haven't used the
<mvc:annotation-driven> directive.

I successfully replicated your scenario and it worked fine using these two libraries and no headers="Accept=*/*" directive.

  • jackson-core-asl-1.7.4.jar
  • jackson-mapper-asl-1.7.4.jar

RestController: HTTP Status 406 - Not Acceptable

Regarding 406 - Not Acceptable

Since your controller is configured to only return a response if the client requests application/json data,

produces = "application/json"

you probably get "406 Not Acceptable" because your client requested something else / did not specify any Accepts header. You can fix by:

  • fixing your client request to provide Accepts header
  • changing the annotation to accept whatever your clients send (inluding */* if you are lazy).

This is your only issue, you are not forced to use any Serialization framework with Spring (But a Serializer is required if you define your controller methods to return arbitrary beans). You can write any String as a response to your clients.

Regarding how to respond with Jsoniter in the background

If you want to keep using Jsoniter, but move that to the background so your Controller class does not explicitly mention jsoniter anymore, you need to define your own custom HttpMessageConverter.

Regarding how to respond without Jsoniter

You need some Serializer to generate Json from Java. You can write custom code, or use Jackson or Gson. Spring supports Jackson by default, and Gson by using GsonHttpMessageConverter. For Jackson, your current code is ok, but you need to add a jackson dependency. For Gson, you need to declare the converter, there are several resources explaining that online.

Regarding how to manually respond with Jsoniter

As in the answers to this question Does spring mvc have response.write to output to the browser directly?

You can write your response using @ResponseBody. Since @RestController already implies @ResponseBody, you could also leave it out. Showing it here just for clarification:

@ResponseBody // not needed with @RestController
@RequestMapping(value = "Test", method = RequestMethod.Get, produces = "application/json")
public String letsTest() {
// Using Jsoniter JsonStream
return JsonStream.serialize(myStringList);
}

406 Not acceptable error when returning JSON in Spring MVC

I could resolve the problem with my above code itself.

Problem was the jackson mapper jar was not present in the classpath.
So it was not working.

In Eclipse For Maven projects you have to force update such that the jar can come in the classpath.

Ideally there should been an error from the spring framework about the class not being loaded. The error itself was misleading.

Best Regards,
Saurav

HTTP Status 406 – Not Acceptable [Streaming huge data from backend using spring 4.3.x + Java 8]

Finally I was able to sort this out which is now helping me stream huge data from backend to Frontend using Spring 4.3.x as mentioned in my post. Below are the points to guide you to execute the program successfully.

Below procedure is so effective that you can even paginate huge data at the back end (can be hibernate, Mongo-java-driver, cassandra java driver, etc)and keep on streaming the data unless your db operation is complete. In some domains like Manufacturing, Insurance, Logistics etc, you need such utility where end user expects quiet huge data from server in the form of CSV, JSON etc to analyse the raw data.

  1. Add one more annotation @EnableWebMvc above your controller class.

  2. When you add above annotation, the code will break at runtime, you can see in catalina.log, this error : java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/util/DefaultIndenter

  3. To fix this you will need to add below jar dependency in your pom.xml

    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
    </dependency>
  4. Now, add <async-supported>true</async-supported> in web.xml under <servlet> tag as below example,

    <servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
    </servlet>

Below is the code for both FILE Streaming Download and Data Stream support.

DATA STREAM CODE:

package com.emg.server.controller.rest;

import java.io.IOException;
import java.io.OutputStream;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

@Controller
@EnableWebMvc
public class StreamRecordsController {

@RequestMapping(value = "/streamrecords")
@ResponseBody
public StreamingResponseBody export() {
return new StreamingResponseBody() {
@Override
public void writeTo (OutputStream out) throws IOException {
for (int i = 0; i < 1000; i++) {
out.write((Integer.toString(i) + " - ")
.getBytes());
out.flush();
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
}
}

FILE STREAM CODE:

package com.emg.server.controller.rest;

import java.io.File;
import java.nio.file.Files;

import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

@Controller
@EnableWebMvc
public class StreamRecordsController {

@RequestMapping(value = "/streamrecords", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
@ResponseBody
public ResponseEntity<StreamingResponseBody> export() {
File file = new File("C:\\Users\\Ankur\\sample.pdf");
StreamingResponseBody responseBody = outputStream -> {
Files.copy(file.toPath(), outputStream);
};
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=generic_file_name.pdf")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(responseBody);
}
}

OUTPUT for both the streaming style

Sample Image

NOTE: I execute both programs individually, deployed and tested it separately.

Spring MVC + JSON = 406 Not Acceptable

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8

That should be the problem. JSON is served as application/json. If you set the Accept header accordingly, you should get the proper response. (There are browser plugins that let you set headers, I like "Poster" for Firefox best)



Related Topics



Leave a reply



Submit