Spring MVC + JSON = 406 不可接受

Posted

技术标签:

【中文标题】Spring MVC + JSON = 406 不可接受【英文标题】:Spring MVC + JSON = 406 Not Acceptable 【发布时间】:2013-04-26 11:32:02 【问题描述】:

我正在尝试生成一个简单的 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。我正在使用 Spring 3.2.2 和 Tomcat 7。

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

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;
    

我已经尝试添加了

<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-asljackson-core-asl 但输出是一样的。

【问题讨论】:

这能帮上忙吗:***.com/questions/2828968/…? 完成那里写的内容后,我不再收到 406 错误,但我也没有收到 JSON 响应。事实上,我没有得到任何回应。服务器返回空文档,状态为 200。 【参考方案1】:

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

Apache Maven 3.0.4 (r1232337; 2012-01-17 01:44:56-0700) org.springframework 版本 3.1.2.RELEASE

spring-security 版本 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>...

【讨论】:

@Dharmesh - 我需要比您在评论中提供的更多信息来帮助您。也许您可以提出一个包含完整详细信息的问题,然后在您的下一条评论中将我引向该问题。祝你好运! 我解决了这个问题。就我而言,这是一个非常小的错误,我没有为我的 Object 类中的 1 个字段提供 getter 和 setter,因为当它尝试使用 @ResponseBody 注释在 JSON 响应正文中转换我的 Object 时,它给了我 406 错误。【参考方案2】:

出现此错误的另一种方法是创建一个没有公共成员的类。在这种情况下,406 unacceptable 是一个非常无用的错误消息。

【讨论】:

【参考方案3】:

接受:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8

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

【讨论】:

它没有改变任何东西,我仍然收到 406 错误。 @Mateusz 您还必须按照 alain.janinm 所写的那样正确设置 spring mvc。最简单的方法是通过&lt;mvc:annotation-driven /&gt;@EnableWebMvc。有关完整的工作示例,请参阅 this previous answer of mine 非常感谢您,通过您之前的帖子,我终于成功了。【参考方案4】:

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

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


【讨论】:

为我解决了这个问题。仍然试图找出原因。似乎this question 描述了它;如果你有一个狭窄的@ContextConfiguration 例如,只有被测类(出于性能原因),如果没有@EnableWebMvc,它可能无法正常找到。【参考方案5】:

其他答案都没有帮助我。

我阅读了几十个关于 406 Not Acceptable、HttpMediaTypeNotAcceptableException、多部分文件、ResponseBody、设置 Accept 标头、生产、消费等的 *** 答案。

我们在 build.gradle 中配置了 SpringBoot 和 Jackson 的 Spring 4.2.4:

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 没有。无论我如何在 SO 尝试不同的解决方案,我总是收到 406 Not Acceptable。

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

阅读 Raniz 的答案和所有的 cmets。

这一切都归结为我们的@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 Not Acceptable。

这是我从 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)。另一种方法是在路径中编码文件名,但我们选择了查询参数方法以避免进一步的副作用。

【讨论】:

【参考方案6】:

在你的 pom 中使用以下依赖项

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

【讨论】:

【参考方案7】:

你必须在你的 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

【讨论】:

【参考方案8】:

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

正确答案的链接:https://***.com/a/21236862/537246

附言

默认是Spring做事的方式,关心的是开发者从头到尾都知道Spring的整个API。然后只是“406不可接受”没有任何细节,Tomcat的日志是空的!

【讨论】:

【参考方案9】:

看到问题出在扩展程序上。根据扩展,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();
    

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

【讨论】:

【参考方案10】:

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

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

我的网址有.html 扩展名。例如:.../getUsers.html。但我在控制器中返回 JSON 数据。 .html 扩展名将默认设置接受类型为 html。

所以我改成如下:

web.xml:

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

网址:

.../getUsers.json

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

【讨论】:

【参考方案11】:

尝试添加

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

getShopInJSON().

它对我有用。

【讨论】:

【参考方案12】:

也许你的 POJO 的所有字段都需要 Getter 和 Setter。

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

而且 406 不是修复错误的有用消息。您应该调试代码并查看异常到底是什么。

【讨论】:

【参考方案13】:

这是因为对象在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

【讨论】:

添加对 gson 的依赖有助于解决我的问题。【参考方案14】:

看起来您正在尝试生成/接收 json 输出。 我发现您的方法存在两个问题。 1)您没有在 Accept 标头中指定 application/json 2)您需要在@RequestMapping 中指定produces="application/json"

【讨论】:

【参考方案15】:

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

【讨论】:

是的。这个对我有用。谢谢 Shubham @RequestMapping(method = RequestMethod.GET,headers = "Accept=text/xml, application/json")【参考方案16】:

我的 RequestMapping 值以 .html 结尾,应该有所不同。

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

【讨论】:

【参考方案17】:

我遇到了类似的问题,用下面的代码解决了

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.

【讨论】:

【参考方案18】:

我的类被JsonSerialize注解了,include参数设置为JsonSerialize.Inclusion.NON_DEFAULT。这导致 Jackson 确定每个 bean 属性的默认值。我有一个返回 int 的 bean 属性。我的问题是 bean getter 调用了一个具有推断返回类型的方法(即:泛型方法)。出于某种奇怪的原因,这段代码被编译了;它不应该编译,因为您不能将 int 用于推断的返回类型。我将该 bean 属性的“int”更改为“Integer”,但不再得到 406。奇怪的是,如果我将 Integer 更改回 int,则代码现在无法编译。

【讨论】:

【参考方案19】:

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

删除 getFoo() 并放入 isFoo()。它对我有用。

【讨论】:

是的。这个对我有用。 @RequestMapping(method = RequestMethod.GET,headers = "Accept=text/xml, application/json")【参考方案20】:

由于这是此错误的最佳答案,我在此处添加 XML 案例。

返回的对象也有可能没有正确定义 XML 结构。对我来说就是这样。

public @ResponseBody DataObject getData(

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

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

【讨论】:

【参考方案21】:

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

<mvc:annotation-driven/>

我一添加它,它就起作用了。

【讨论】:

【参考方案22】:

我也遇到了同样的“406”错误,但是根本原因和Spring的object => json策略有关……

春季:3.2.2.发布

雄猫:7

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

EmployeeProfileResponseData.java 段


    private Boolean profileActive;

    public Boolean isProfileActive() 
        return this.profileActive;
    

    public Boolean getProfileActive() 
        return this.profileActive;
    

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

如果可怜的 POJO 包含公共方法 isProfileActive,它总是会返回 406。

但如果我删除了该方法,它会返回正常的 JSON。

所以我尝试将方法重命名为isMyProfileActive,然后我发现返回的JSON包含一个新属性:


    ...
    "profileActive": true,
    "myProfileActive": true

然后我意识到发生了什么。

当 POJO 包含公共方法 isProfileActive 时,Spring 会相应地生成一个属性“profileActive”。但是,JSON 已经包含同名的属性(根据公共 getter/setter 方法)。所以这两个属性造成了冲突,Tomcat不会返回正常的JSON。

这正是“406”代码的含义。

超文本传输​​协议 (HTTP) 406 Not Acceptable 客户端错误响应代码表示服务器无法生成与请求的主动内容协商标头中定义的可接受值列表匹配的响应,并且服务器不愿意提供默认值表示。

所以最后我删除了isProfileActive 方法,一切正常。

顺便说一句,isProfileActive 方法是由 vscode 生成的,我的同事推送了该代码并没有太在意。

【讨论】:

以上是关于Spring MVC + JSON = 406 不可接受的主要内容,如果未能解决你的问题,请参考以下文章

Spring4 MVC json问题(406 Not Acceptable)

406 Spring MVC Json,根据请求“接受”标头不可接受

springmvc怎么返回json数据报406

jsckson,想说爱你不容易啊。。。406错误

Spring MVC - 将对象返回为@ResponseBody 时出现问题(著名的错误 406,未解决)

mvc:annotation-driven 不能解决 406 错误