2

My problem is that I want to return a Java collection as JSON using Spring MVC and the standard annotations that come with it. Unfortunately though I end up with HTTP error code 406 every time.

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

I thought that Jackson and Spring can automagically convert my return type but it returns a HTTP error 406 if we use a collection or anything other return type than String. Can you please help show what I am doing wrong?


Research
As I mentioned we can get a String to return. We think the problem will be either in Maven (dependencies with Jackson) or in our understanding of Spring MVC REST annotations. Another route is we have not configured our Spring context's correctly.


Controller
This is my annotated Spring controller. The first simple method (getAJsonString) works but the second one (getImprovedJsonMap) with a map returns a HTTP error code 406 which we presume is due to the complex type:
@Controller
@RequestMapping("/kfc/brands")
public class DefaultMetricsService {

    @RequestMapping(value="/firstMethod", method = RequestMethod.GET)
    public @ResponseBody String getAJsonString() {
        return "HELLO";
    }

    @RequestMapping(value = "/Metrics/type3", method = RequestMethod.GET)
    public @ResponseBody Map<String, String> getImprovedJsonMap() {
        Map jsonMap = new HashMap<String, String>();
        jsonMap.put("counter", "3000");
        return jsonMap;
    }


Spring Servlet Definition
This is my Spring configuration (metrics-servlet.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:mvc="http://www.springframework.org/schema/mvc"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xsi:schemaLocation=
           "http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.1.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">

    <context:component-scan base-package="com.t2" />

    <mvc:annotation-driven />


</beans>
...


Dependencies
This is my pom with versions of Jackson:
<!-- Dependency Versions -->
    <org.slf4j.version>1.6.1</org.slf4j.version>
    <spring.version>3.2.2.RELEASE</spring.version>
    <commons.loggin.version>1.1.1</commons.loggin.version>
    <junit.version>4.8.2</junit.version>
    <jackson.version>1.9.10</jackson.version>    

 ...
        <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
        <exclusions>
            <!-- Exclude Commons Logging in favor of SLF4j -->
            <exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>${commons.loggin.version}</version>
    </dependency>

    <dependency>
        <groupId>commons-lang</groupId>
        <artifactId>commons-lang</artifactId>
        <version>2.6</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>

    <!-- Jackson JSON Mapper -->
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-mapper-asl</artifactId>
        <version>${jackson.version}</version>
    </dependency>

    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-core-asl</artifactId>
        <version>${jackson.version}</version>
    </dependency>
...


Servlet
This is my servlet definition:
    <servlet>
    <servlet-name>metrics</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:metrics-application-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>metrics</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:metrics-servlet.xml</param-value>
</context-param>

<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>
Anto
  • 4,265
  • 14
  • 63
  • 113

3 Answers3

2

I think you might have a problem with content negotiation in Spring 3.2. See Spring documentation

Try disabling it in your metrics-servlet.xml

<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="favorPathExtension" value="false" />
</bean>
dimchez
  • 2,004
  • 1
  • 15
  • 9
  • I have added this bean in metrics-servlet.xml but the error still occurs – Anto Sep 19 '13 at 10:23
  • 1
    @Anto I think you should also check that type that you are trying to return can be serialized. See [this answer](http://stackoverflow.com/a/7474081/595072) – dimchez Sep 19 '13 at 18:23
1

Looks like you haven't initialized the Jackson serializer in the spring context.

Try adding the following line to your metrics-servlet.xml:

    <bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
</beans>

And just for reference, my spring servlet is using mvc, beans and context versions 3.2.

Hope this helps!

sirugh
  • 324
  • 2
  • 11
0

You need to add produces="application/json" to your @RequestMapping so that the response is identified as JSON.

@RequestMapping(value = "/Metrics/type3", method = RequestMethod.GET, produces="application/json")
Qwerky
  • 18,217
  • 6
  • 44
  • 80
  • I thought the existence of Jackson on the classpath set response type to *some default* JSON representation... by default...? – Crowie Sep 19 '13 at 09:30
  • 1
    Even with the produces="application/json" the problem persists – Anto Sep 19 '13 at 10:22