Spring MVC + JSON = 406 Not Acceptable

JavaJsonSpring

Java Problem Overview


I'm trying to generate a simple JSON response working. Right now I get 406 Not Acceptable error. Tomcat says "The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers." even though my Accept headers are

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

In tomcat/lib I have all Tomcat jars, Spring jars and jackson-all-1.9.0.jar. I'm using Spring 3.2.2 with Tomcat 7.

I'm aware that this issue has been discussed many times, but none of solutions is working for me.

web.xml

<web-app id="WebApp_ID" version="2.4" 
	xmlns="http://java.sun.com/xml/ns/j2ee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
	http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 
  <display-name>Spring Web MVC Application</display-name>
 
  <servlet>
  	<servlet-name>dispatcher</servlet-name>
        <servlet-class>
                  org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
  </servlet>
 
  <servlet-mapping>
 	<servlet-name>dispatcher</servlet-name>
        <url-pattern>*.htm</url-pattern>
  </servlet-mapping>
 
</web-app>

dispatcher-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	 http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
 
    <bean id="viewResolver"
    	class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
        <property name="prefix">
            <value>/WEB-INF/pages/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>
 <context:component-scan base-package="com.smiechmateusz.controller" />
 <context:annotation-config />
 
	<mvc:annotation-driven />
 
</beans>

HelloWorldController.java

package com.smiechmateusz.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

import com.smiechmateusz.dao.Foo;

@Controller
@RequestMapping("/")
public class HelloWorldController extends AbstractController{
 
	@Override
	protected ModelAndView handleRequestInternal(HttpServletRequest request,
		HttpServletResponse response) throws Exception {
 
		ModelAndView model = new ModelAndView("HelloWorldPage");
		return model;
	}
	
	@RequestMapping(value="foobar.htm", method = RequestMethod.GET)
	public @ResponseBody Foo getShopInJSON() {
 		Foo f = new Foo();
		f.setX(1);
		f.setY(2);
		f.setDescription("desc");
		return f;
 	}
}

Foo.java

package com.smiechmateusz.dao;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="foobaz")
public class Foo implements Serializable
{
	private int x, y;
	String description;
	int id;
	
	@Column(name = "x")
	public int getX() {
		return x;
	}
	public void setX(int x) {
		this.x = x;
	}
	@Column(name = "y")
	public int getY() {
		return y;
	}
	public void setY(int y) {
		this.y = y;
	}
	@Column(name = "description")
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}
	
	@Id @GeneratedValue
	@Column(name = "id")
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
}

I've already tried adding

<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
          <list>
            <ref bean="jsonConverter"/>
          </list>
    </property>
</bean>

to my dispatcher-servlet.xml or changing jakcson-all to jackson-asl and jackson-core-asl but output was the same.

Java Solutions


Solution 1 - Java

If you're using Maven and the latest [Jackson code][1] then you can remove all the Jackson-specific configuration from your spring configuration XML files (you'll still need an annotation-driven tag <mvc:annotation-driven/>) and simply add some Jackson dependencies to your pom.xml file. See below for an example of the dependencies. This worked for me and I'm using:

  • Apache Maven 3.0.4 (r1232337; 2012-01-17 01:44:56-0700)

  • org.springframework version 3.1.2.RELEASE

  • spring-security version 3.1.0.RELEASE.

     ...<dependencies>
     ...
     	<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>
     	...
     </dependencies>...
    

[1]: https://github.com/FasterXML/jackson "Jackson Home Page"

Solution 2 - Java

One more way you can get this error is to create a class with no public members. 406 unacceptable is a pretty useless error message in this scenario.

Solution 3 - Java

> 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)

Solution 4 - Java

With Spring 4 you only add @EnableWebMvc, for example:

@Controller
@EnableWebMvc
@RequestMapping(value = "/articles/action", headers="Accept=*/*",  produces="application/json")
public class ArticlesController {

}

Solution 5 - Java

None of the other answers helped me.

I read dozens of Stackoverflow answers about 406 Not Acceptable, HttpMediaTypeNotAcceptableException, multipart file, ResponseBody, setting Accept headers, produces, consumes etc.

We had Spring 4.2.4 with SpringBoot and Jackson configured in build.gradle:

compile "com.fasterxml.jackson.core:jackson-core:2.6.7"
compile "com.fasterxml.jackson.core:jackson-databind:2.6.7"

All routes worked fine in our other controllers and we could use GET, POST, PUT, and DELETE. Then I started to add multipart file upload capability and created a new controller. The GET routes work fine but our POST and DELETE didn't. No matter how I tried different solutions from here at SO I just kept getting 406 Not Acceptable.

Then finally I stumbled across this SO answer: https://stackoverflow.com/questions/30793717/spring-throwing-httpmediatypenotacceptableexception-could-not-find-acceptable-r

Read Raniz's answer and all the comments.

It all boiled down to our @RequestMapping values:

@RequestMapping(value = "/audio/{fileName:.+}", method = RequestMethod.POST, consumes="multipart/*")
public AudioFileDto insertAudio(@PathVariable String fileName, @RequestParam("audiofile") MultipartFile audiofile) {

    return audioService.insert(fileName, audiofile);
}

@RequestMapping(value = "/audio/{fileName:.+}", method = RequestMethod.DELETE)
public Boolean deleteAudio(@PathVariable String fileName) {

    return audioService.remove(fileName);
}

The {fileName:.+} part in a @RequestMapping value caused the 406 Not Acceptable in our case.

Here's the code I added from Raniz's answer:

@Configuration
public class ContentNegotiationConfig extends WebMvcConfigurerAdapter {
    @Override
    void configureContentNegotiation(final ContentNegotiationConfigurer configurer) {
        // Turn off suffix-based content negotiation
        configurer.favorPathExtension(false);
    }
}

EDIT August 29th 2016:

We got into trouble using configurer.favorPathExtension(false): static SVG images ceased to load. After analysis, we found that Spring started to send SVG files back to UI with content-type "application/octet-stream" instead of "image/svg+xml". We solved this by sending fileName as a query parameter, like:

@RequestMapping(value = "/audio", method = RequestMethod.DELETE)
public Boolean deleteAudio(@RequestParam String fileName) {

    return audioService.remove(fileName);
}

We also removed the configurer.favorPathExtension(false). Another way could be to encode fileName in the path but we chose the query parameter method to avoid further side effects.

Solution 6 - Java

Use below dependency in your pom

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

Solution 7 - Java

You have to register the annotation binding for Jackson in your spring-mvc-config.xml, for example :

<!-- activates annotation driven binding -->
<mvc:annotation-driven ignoreDefaultModelOnRedirect="true" validator="validator">
	<mvc:message-converters>
		<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
		<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/>
		<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
	</mvc:message-converters>
</mvc:annotation-driven>

Then in your controller you can use :

@RequestMapping(value = "/your_url", method = RequestMethod.GET, produces = "application/json")
@ResponseBody

Solution 8 - Java

I suppose, that the problem was in usage of *.htm extension in RequestMapping (foobar.htm). Try to change it to footer.json or something else.

The link to the correct answer: https://stackoverflow.com/a/21236862/537246

P.S.

It is in manner of Spring to do something by default, concerning, that developers know whole API of Spring from A to Z. And then just "406 not acceptable" without any details, and Tomcat's logs are empty!

Solution 9 - Java

See the problem is with the extension. Depending upon the extension, spring could figure out the content-type. If your url ends with .com then it sends text/html as the content-type header. If you want to change this behavior of Spring, please use the below code:

@Configuration
@Import(HibernateConfig.class)
@EnableWebMvc
// @EnableAsync()
// @EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.azim.web.service.*",  basePackageClasses = { WebSecurityConfig.class }, excludeFilters = { @ComponentScan.Filter(Configuration.class) })
public class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
		configurer.favorPathExtension(false).favorParameter(true).parameterName("mediaType").ignoreAcceptHeader(true).useJaf(false)
				.defaultContentType(MediaType.APPLICATION_JSON).mediaType("xml", MediaType.APPLICATION_XML).mediaType("json", MediaType.APPLICATION_JSON);
	}

	@Bean(name = "validator")
	public Validator validator() {
		return new LocalValidatorFactoryBean();
	}
}

Here, we are setting favorPathExtension to false and Default Content-type to Application/json. Note: HibernateConfig class contains all the beans.

Solution 10 - Java

Today I too gone through the same issue. In my case, in web.xml I have

   <servlet-mapping>
		<servlet-name>spring</servlet-name>
		<url-pattern>*.html</url-pattern>
	</servlet-mapping>

my url is having .html extension. Ex:.../getUsers.html. But I am returning JSON data in controller. .html extension will by default set accept type as html.

So I changed to the following:

web.xml:

<servlet-mapping>
	<servlet-name>spring</servlet-name>
	<url-pattern>*.html</url-pattern>
    <url-pattern>*.json</url-pattern>
</servlet-mapping>

URL:

.../getUsers.json

Everything is working fine now. Hope it helps.

Solution 11 - Java

Try adding

@RequestMapping(method = RequestMethod.GET,headers = {"Accept=text/xml, application/json"})

on getShopInJSON().

It worked for me.

Solution 12 - Java

Maybe all fields of your POJO need Getter and Setter.

I fixed it according to this issue. reference:https://stackoverflow.com/questions/7197268/spring-mvc-httpmediatypenotacceptableexception

And 406 is not a useful message to fix the bug. You should debug into the codes and see what the Exception is on earth.

Solution 13 - Java

this is because of the object is not acceptable at jsp... use his

add this dependency or any other send converted json string to jsp...

for example add this in pom

<dependency>
    	<groupId>com.google.code.gson</groupId>
    	<artifactId>gson</artifactId>
    	<version>2.6.2</version>
	</dependency>

and use code like that:

@RequestMapping(value="foobar.htm", method = RequestMethod.GET)
    public @ResponseBody String getShopInJSON() {
        Foo f = new Foo();
        f.setX(1);
        f.setY(2);
        f.setDescription("desc");
        return new Gson().toJson(f); //converted object into json string
    }//return converted json string

Solution 14 - Java

It looks like your are trying to produce/recieve json output. I see two problems with your approach.

  1. You didn't specify the application/json in your Accept header
  2. You need to specify produces="application/json" in your @RequestMapping

Solution 15 - Java

There is another case where this status will be returned: if the Jackson mapper cannot figure out how to serialize your bean. For example, if you have two accessor methods for the same boolean property, isFoo() and getFoo().

removed getFoo() and put isFoo(). it worked for me.

Solution 16 - Java

I couldn't see it as an answer here so I thought I would mention that I recieved this error using spring 4.2 when I accidentally removed the getter/setter for the class I was expecting to be returned as Json.

Solution 17 - Java

My RequestMapping value was ending with .html which should be something different.

I tried changing it to .json and it worked for me.

Solution 18 - Java

I have got the similar issue and resolved by using below code

public class ProductList {

	private List<Product> productList =  new ArrayList<Product>();

	@JsonProperty("data")
	@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT)
	public List<Product> getProductList() {
		return productList;
	}
public void setProductList(List<Product> productList) {
		this.productList = productList;
	}

I am setting ProductList object in ResponseEntity object and returning from controller.

Solution 19 - Java

My class was annotated with JsonSerialize, and the include parameter was set to JsonSerialize.Inclusion.NON_DEFAULT. This caused Jackson to determine the default values for each bean property. I had a bean property that returned an int. The problem in my case was that the bean getter made a call to a method which has an inferred return type (i.e.: a generic method). For some odd reason this code compiled; it shouldn't have compiled because you cannot use an int for an inferred return type. I changed the 'int' to an 'Integer' for that bean property and I no longer got a 406. The odd thing is, the code now fails to compile if I change the Integer back to an int.

Solution 20 - Java

As it is the top answer for this error, I am adding the case for XML here.

There is also a possibility that the object returned hasn't correctly defined XML structure. That was the case for me.

public @ResponseBody DataObject getData(

Was throwing the same error despite correct headers. The errors stopped when I added @XmlRootElement to the header of the object:

@XmlRootElement
public class DataObject {
    @XmlElement(name = "field", nillable = true)
    protected String field;

Solution 21 - Java

I had the same issue, in my case the following was missing from my xxx-servlet.xml config file

<mvc:annotation-driven/>

As soon I added this it it worked.

Solution 22 - Java

I encountered the same "406" error, but the root cause is related to Spring's object => json strategy...

Spring: 3.2.2.RELEASE

Tomcat: 7

@RequestMapping(value = "/employee/search/v2/{employeeId}")
    public ResponseEntity<EmployeeProfileResponseData> searchEmployeeV2(@PathVariable String employeeId) 

EmployeeProfileResponseData.java segment


    private Boolean profileActive;

    public Boolean isProfileActive() {
        return this.profileActive;
    }

    public Boolean getProfileActive() {
        return this.profileActive;
    }

    public void setProfileActive(Boolean profileActive) {
        this.profileActive = profileActive;
    }

If the poor POJO contains the public method isProfileActive, it would always return 406.

But if I removed the method, it returned normal JSON.

So I tried to rename the method as isMyProfileActive, then I found the return JSON contained a new attribute:

{
    ...
    "profileActive": true,
    "myProfileActive": true
}

Then I realised what happened.

When the POJO contained the public method isProfileActive, Spring would generate an attribute 'profileActive' accordingly. However, the JSON already contained an attribute with the same name (according to the public getter/setter method). So these two attributes caused a conflict, Tomcat wouldn't return a normal JSON.

That's exactly what a "406" code means.

> The HyperText Transfer Protocol (HTTP) 406 Not Acceptable client error response code indicates that the server cannot produce a response matching the list of acceptable values defined in the request's proactive content negotiation headers, and that the server is unwilling to supply a default representation.

So finally I removed the isProfileActive method, everything fine.

BTW, the isProfileActive method is generated by vscode, my colleague pushed that code without too much concern.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMateuszView Question on Stackoverflow
Solution 1 - JavaKSevView Answer on Stackoverflow
Solution 2 - JavaDoo DahView Answer on Stackoverflow
Solution 3 - JavaSean Patrick FloydView Answer on Stackoverflow
Solution 4 - JavaNguyen VietView Answer on Stackoverflow
Solution 5 - JavaJarno ArgillanderView Answer on Stackoverflow
Solution 6 - JavaNirbhay RanaView Answer on Stackoverflow
Solution 7 - Javaalain.janinmView Answer on Stackoverflow
Solution 8 - JavaDemwisView Answer on Stackoverflow
Solution 9 - JavaAzimView Answer on Stackoverflow
Solution 10 - JavavissuView Answer on Stackoverflow
Solution 11 - JavaShubhamView Answer on Stackoverflow
Solution 12 - JavaDai KaixianView Answer on Stackoverflow
Solution 13 - Javaaditya GoyalView Answer on Stackoverflow
Solution 14 - JavaBalaji KatikaView Answer on Stackoverflow
Solution 15 - JavaThe Java GuyView Answer on Stackoverflow
Solution 16 - JavajwvView Answer on Stackoverflow
Solution 17 - JavaSwapneelView Answer on Stackoverflow
Solution 18 - JavaSachinView Answer on Stackoverflow
Solution 19 - JavaJared SimonView Answer on Stackoverflow
Solution 20 - JavabmaggotView Answer on Stackoverflow
Solution 21 - Javayla-devView Answer on Stackoverflow
Solution 22 - Javakarl liView Answer on Stackoverflow