Spring MVC + JSON = 406不可接受

时间:2013-05-02 10:21:34

标签: java json spring

我正在尝试生成一个简单的JSON响应。现在我得到406 Not Acceptable错误。 Tomcat说:“此请求标识的资源只能根据请求”接受“标题生成具有不可接受特征的响应。”即使我的Accept标题是

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

在tomcat / lib中我有所有Tomcat jar,Spring jar和jackson-all-1.9.0.jar。我在Tomcat 7中使用Spring 3.2.2。

我知道这个问题已经多次讨论过了,但没有一个解决方案适合我。

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

调度-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;
    }
}

我已经尝试添加

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

到我的 dispatcher-servlet.xml 或将 jakcson-all 更改为 jackson-asl jackson-core-asl < / em>但输出相同。

21 个答案:

答案 0 :(得分:38)

如果你正在使用Maven和最新的Jackson code,那么你可以从你的弹簧配置XML文件中删除所有特定于Jackson的配置(你仍然需要一个注释驱动的标签&lt; mvc:annotation-驱动/&gt;)并简单地将一些Jackson依赖项添加到您的pom.xml文件中。请参阅下面的依赖项示例。这对我有用,我正在使用:

  • Apache Maven 3.0.4(r1232337; 2012-01-17 01:44:56-0700)
  • org.springframework版本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 :(得分:20)

您可以获得此错误的另一种方法是创建一个没有公共成员的类。在这种情况下,406不可接受是一个非常无用的错误消息。

答案 2 :(得分:17)

  

接受:text / html的,应用/ XHTML + xml的,应用/ XML; Q = 0.9, / 的; Q = 0.8

这应该是问题所在。 JSON作为application/json提供。如果相应地设置Accept标头,则应该得到正确的响应。 (有浏览器插件可以让你设置标题,我最喜欢Firefox的“海报”)

答案 3 :(得分:9)

其他答案都没有帮助我。

我阅读了大量有关406 Not Acceptable,HttpMediaTypeNotAcceptableException,multipart文件,ResponseBody,设置Accept标头,生成,消耗等的Stackoverflow答案。

我们使用Spring 4.2和Spring配置了Spring.Boot:

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

我们的其他控制器中的所有路由都运行正常,我们可以使用GET,POST,PUT和DELETE。然后我开始添加多部分文件上传功能并创建一个新的控制器。 GET路由工作正常但我们的POST和DELETE没有。无论我如何从这里尝试不同的解决方案,我只是不断获得406不可接受。

然后我终于偶然发现了这个答案: Spring throwing HttpMediaTypeNotAcceptableException: Could not find acceptable representation due to dot in url path

阅读Raniz的回答和所有评论。

这一切都归结为我们的@RequestMapping值:

@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);
}

@RequestMapping值中的{fileName:.+}部分导致我们的情况下406不可接受。

这是我从Raniz的回答中添加的代码:

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

编辑2016年8月29日:

我们使用configurer.favorPathExtension(false)遇到了麻烦:静态SVG图片停止加载。经过分析,我们发现Spring开始将SVG文件发送回UI,内容类型为“application / octet-stream”,而不是“image / svg + xml”。我们通过将fileName作为查询参数发送来解决此问题,例如:

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

    return audioService.remove(fileName);
}

我们还删除了configurer.favorPathExtension(false)。另一种方法是在路径中编码fileName,但我们选择查询参数方法以避免进一步的副作用。

答案 4 :(得分:9)

使用Spring 4,您只需添加@EnableWebMvc,例如:

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

}

答案 5 :(得分:6)

在你的pom中使用以下依赖

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

答案 6 :(得分:5)

您必须在spring-mvc-config.xml中注册Jackson的注释绑定,例如:

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

然后在你的控制器中你可以使用:

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

答案 7 :(得分:4)

我想,问题是在RequestMapping( foobar.htm )中使用 * .htm 扩展名。尝试将其更改为 footer.json 或其他内容。

指向正确答案的链接:https://stackoverflow.com/a/21236862/537246

P.S。

默认情况下,Spring的方式是开发人员从A到Z知道Spring的整个API。然后只是&#34; 406不能接受&#34;没有任何细节,Tomcat的日志都是空的!

答案 8 :(得分:2)

看问题是扩展名。根据扩展名,spring可以找出内容类型。如果您的网址以 .com 结尾,则会将 text / html 作为内容类型标头发送。如果要更改Spring的此行为,请使用以下代码:

@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();
    }
}

在这里,我们将favorPathExtension设置为false,将Default Content-type设置为Application / json。 注意: HibernateConfig类包含所有bean。

答案 9 :(得分:2)

今天我也经历了同样的问题。在我的情况下,在web.xml我有

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

我的网址有.html分机。实施例:.../getUsers.html。但是我在控制器中返回JSON数据。 .html扩展名默认将accept类型设置为html。

所以我改为:

<强>的web.xml:

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

<强> URL:

.../getUsers.json

现在一切正常。希望它有所帮助。

答案 10 :(得分:1)

尝试添加

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

on getShopInJSON()

它对我有用。

答案 11 :(得分:1)

也许你的POJO的所有领域都需要Getter和Setter。

我根据这个问题修复了它。  参考:Spring MVC - HttpMediaTypeNotAcceptableException

406并不是修复bug的有用信息。您应该调试代码并查看地球上的异常情况。

答案 12 :(得分:1)

这是因为在jsp中对象是不可接受的...使用他的

将此依赖项或任何其他发送转换的json字符串添加到jsp ...

例如,

在pom中添加此内容

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

并使用类似的代码:

@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

答案 13 :(得分:0)

还有另一种情况会返回此状态:如果Jackson映射器无法弄清楚如何序列化bean。例如,如果对同一布尔属性有两个访问器方法,isFoo()getFoo()

已移除getFoo()并放置isFoo()。它对我有用。

答案 14 :(得分:0)

我无法在这里看到它作为答案所以我想我会提到我使用Spring 4.2收到了这个错误,因为我不小心删除了我希望作为Json返回的类的getter / setter。

答案 15 :(得分:0)

看起来您正在尝试生成/接收json输出。 我看到你的方法有两个问题。 1)您没有在Accept标头中指定application / json 2)您需要指定produce =&#34; application / json&#34;在你的@RequestMapping

答案 16 :(得分:0)

我的RequestMapping值以 .html 结尾,这应该是不同的。

我尝试将其更改为 .json ,这对我有用。

答案 17 :(得分:0)

我遇到了类似的问题,并使用下面的代码

解决了
<tr>

答案 18 :(得分:0)

我的类使用JsonSerialize注释,include参数设置为JsonSerialize.Inclusion.NON_DEFAULT。这导致Jackson确定每个bean属性的默认值。我有一个返回int的bean属性。我的问题是bean getter调用了一个具有推断返回类型的方法(即:一般方法)。由于某些奇怪的原因,这个代码编译它不应该编译,因为你不能使用int作为推断的返回类型。我改变了&#39; int&#39;到一个整数&#39;对于那个bean属性,我不再有406.奇怪的是,如果我将Integer更改回int,代码现在无法编译。

答案 19 :(得分:0)

因为它是此错误的最佳答案,所以我在这里添加XML的大小写。

返回的对象还可能没有正确定义XML结构。对我来说就是这种情况。

public @ResponseBody DataObject getData(

尽管标头正确,也会抛出相同的错误。 当我将@XmlRootElement添加到对象的标题时,错误停止了:

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

答案 20 :(得分:0)

我遇到了同样的问题,就我而言,我的xxx-servlet.xml配置文件中缺少以下内容

<mvc:annotation-driven/>

我一经添加就可以了。