HTTP Status 406. Spring MVC 4.0, jQuery, JSON

21,515

Solution 1

The main issue here is that the path "/test.htm" is going to use content negotiation first before checking the value of an Accept header. With an extension like *.htm, Spring will use a org.springframework.web.accept.ServletPathExtensionContentNegotiationStrategy and resolve that the acceptable media type to return is text/html which does not match what MappingJacksonHttpMessageConverter produces, ie. application/json and therefore a 406 is returned.

The simple solution is to change the path to something like /test, in which content negotiation based on the path won't resolve any content type for the response. Instead, a different ContentNegotiationStrategy based on headers will resolve the value of the Accept header.

The complicated solution is to change the order of the ContentNegotiationStrategy objects registered with the RequestResponseBodyMethodProcessor which handles your @ResponseBody.

Solution 2

I had the same problem in the end it was the version of org.codehaus.jackson 1.9.x, when I switched from 1.9.x jackson to 2.x (fasterxml) in my pom.xml

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

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

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.2.3</version>

</dependency>

also is necesary : <mvc:annotation-driven />

Solution 3

I ran into this issue when upgrading Spring in a legacy project. The .html suffix of the AJAX endpoints (which were trying to return JSON) were indeed forcibly made to try to produce HTML due to the suffix, and since the handlers were returning an object, the request ended in a 406 error since Spring couldn't figure out how to make HTML out of a plain Java object.

Instead of altering the endpoint suffixes, or doing a complete configuration of a custom ContentNegotiationStrategy, making this change was enough:

<mvc:annotation-driven />

changed to:

<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="favorPathExtension" value="false" />
</bean>
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
Share:
21,515
Shatranaft
Author by

Shatranaft

Updated on May 03, 2020

Comments

  • Shatranaft
    Shatranaft about 4 years

    I want to send JSON from my controller. I have the following configuration.

    spring-servlet.xml :

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
               http://www.springframework.org/schema/mvc
               http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
        <mvc:annotation-driven/>
    
        <mvc:resources mapping="/resources/**" location="/resources/"/>
        <context:component-scan base-package="com.castle.controllers"/>
    
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
            <property name="prefix" value="/views/"/>
            <property name="suffix" value=".jsp"/>
        </bean>
    </beans>
    

    .js :

    function testAjax() {
        var data = {userName: "MyUsername", password:"Password"};
        $.ajax({
            url: 'ajax/test.htm',
            dataType : 'json',
            type : 'POST',
            contentType: "application/json",
            data: JSON.stringify(data),
            success: function(response){
                alert('Load was performed.');
            }
        });
    }
    

    UserTest.java:

    public class UserTest {
        private String userName;
        private String password;
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }
    

    TestAjaxController.java :

    @Controller
    @RequestMapping("/ajax")
    public class TestAjaxController {
    
        @RequestMapping(method = RequestMethod.POST, value = "/test.htm")
        public @ResponseBody
        UserTest testAjaxRequest(@RequestBody UserTest user) {
            return user;
        }
    }
    

    pom.xml :

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

    When i do this request, i get in my Controller JSON represented as UserTest object. But on return :

    HTTP Status 406 - The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.

    What i'm doing wrong? I know, there is a lot of questions about such cases, but i can't fix it for 2 days...

    UPDATE I Have found the solution!! It's only need to return an Object. Not a User object or something. But return Object;

     public @ResponseBody Object testAjaxRequest(@RequestBody UserTest user) {
            List<UserTest> list = new ArrayList<>();
            list.add(user);
            list.add(user);
            list.add(user);
            return list;
    
  • Sotirios Delimanolis
    Sotirios Delimanolis over 10 years
    As I've explained in a comment to Pracede's answer, these @RequestMapping attribute values only restrict what the handler method is mapped to. By not specifying that value, you are already covering it, so it isn't explicitly necessary in this case.
  • Assaf Moldavsky
    Assaf Moldavsky about 8 years
    Thank you very much for suggesting to look into ContentNegotiationStrategy!!
  • Christian
    Christian over 6 years
    I was about to go insane. First time i read about ContentNegotiation. I had the same problem, legacy app with .html endings sending out json. It's crazy. You pointed me in the right direction and finally solved my issue. Whenever you are in Germany, ping me and you'll have access to an infinite beer pipeline. This blog also helped me, after knowing the terms: spring.io/blog/2013/05/11/content-negotiation-using-spring-m‌​vc