2

I use "Jsonitter" as a JSON serialization framework and I don't use Maven in my project. I've been returning JSON objects in in my restful api by directly writing the result of the "Jsonitter" to the HttpServletResponse until now that I found about the @RestController attribute. Being from an ASP.Net MVC background, I expect the framework to automatically serialize the returned object in my api in accordance with the Accept header. But I feel like, Spring requires a third-party serialization framework to render the result (i.e. Jackson) because it returns HTTP Status 406 - Not Acceptable result instead.

The way I'm using it is as follows:

@RestController
@EnableWebMvc
public class TestApi {
    @RequestMapping(value = "Test", method = RequestMethod.Get, produces = "application/json")
    public List<String> letsTest(){
        return myStringList;
    }
}

I don't have any reference to Jackson and I'd rather not use it at all and I feel like the error is due to that. Is there any way to get this work without Jackson?

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
Arnold Zahrneinder
  • 4,788
  • 10
  • 40
  • 76

3 Answers3

5

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);
}
tkruse
  • 10,222
  • 7
  • 53
  • 80
  • I would go with custom Jsoniter message converter rather than converting the value in controller itself. – Alexander.Furer Dec 18 '17 at 05:06
  • I downvoted this because I described in the question that I don't wanna do this (this is my current approach). So although I'm thankful for your contribution, this answer is absolutely irreverent – Arnold Zahrneinder Dec 18 '17 at 05:38
  • @tkruse: The thing is that I don't wanna use jackson, I wanna use Jsoniter as a config to bring about a RestController behavior. – Arnold Zahrneinder Dec 18 '17 at 07:15
  • @tkruse: Yeah that's right, but instead of writing the json manually to the response stream either by the annotation attribute or directly via Servlet, I want to configure the Spring framework in a way that I can use RestController annotation and return the object directly in the API. For example I return `List` and Spring handles the serialization based on the provided serializer framework in the background. – Arnold Zahrneinder Dec 18 '17 at 11:55
  • Added another answer section for that. Note this has nothing to do with your 406 http error though, so if this is not enough information and the linked question does not help, maybe you can create a new question about custom HttpMessageConverters. – tkruse Dec 18 '17 at 13:02
  • @tkruse: I changed my downvote to an upvote. Thank you very much indeed for your priceless help. – Arnold Zahrneinder Dec 18 '17 at 13:40
1

Spring 4.3.10: I used the below settings to resolve the issue.

Step 1: Add the below dependencies

    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.6.7</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.6.7</version>
</dependency>
<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-core-asl</artifactId>
    <version>1.9.13</version>
</dependency>
<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>1.9.13</version>
</dependency>

Step 2: Add the below in your MVC DispatcherServlet context configuration:

<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>

<bean id="contentNegotiationManager"
        class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
        <property name="favorPathExtension" value="false"/>
        <property name="favorParameter" value="true"/>
        <property name="ignoreAcceptHeader" value="false" />
    </bean>

Since spring 3.2, as per the default configuration favorPathExtension is set as true, because of this if the request uri have any proper extensions like .htm spring will give priority for the extension. In step 2 I had added the contentNegotiationManager bean to override this.

0

In my Case the scenario was to insert pdf resources in the database using the Spring restTemplate.put method.

http://${apiGateWay}/resource/provision/Test.pdf

with header as application/pdf. But Still I was getting 406 not acceptable error.

After debugging the issue it was found out that,put request does not accept the key with . extension.

Hence the url should be,

http://${apiGateWay}/resource/provision/Test

Chetan T
  • 11
  • 3