Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTTP Status 406. Spring MVC 4.0, jQuery, JSON

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;
like image 970
Shatranaft Avatar asked Nov 30 '22 11:11

Shatranaft


1 Answers

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.

like image 189
Sotirios Delimanolis Avatar answered Dec 15 '22 07:12

Sotirios Delimanolis