“406 不可接受"尝试通过 HTTP 为 Spring Web MVC 服务配置 XML 编组时 [英] "406 Not Acceptable" when trying to configure XML marshalling over HTTP for a Spring Web MVC service

查看:39
本文介绍了“406 不可接受"尝试通过 HTTP 为 Spring Web MVC 服务配置 XML 编组时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

github 上有一个捕捉我遇到的问题的玩具示例.简而言之,我有一个服务需要通过 HTTP 封送 XML.我正在尝试使用 Spring Web MVC 进行设置.需要重新编组的响应对象是从架构中生成的 JAXB2(因此缺少 @XmlRootElement 标记,但生成它的包有一个 ObjectFactory,它提供了生成 JAXBElement-s 的方法,这使 XML 编组器满意).我尝试了基于 Google 搜索的不同 spring 上下文配置,这些配置主要是在堆栈溢出时点击此处的帖子,但无法让它们中的任何一个对我有用.

A toy example that captures the issue I am having is on github. In a nutshell I have a service that needs to marshal XML back over HTTP. I am trying to set that up using Spring Web MVC. The response object that needs to get marshalled back is JAXB2-generated from a schema (thus is missing @XmlRootElement tag but the package where it is generated has an ObjectFactory which provides the means to generate JAXBElement-s, which make XML marshallers happy). I tried different spring context configurations based on Google searches, which mostly hit posts here on stack overflow, but could not get any of them to work for me.

环境:

  • Spring 4.0.5.
  • Tomcat 7.0.5X.

这是一个显示问题的请求/响应循环示例(某些输出已被省略):

Here's a request/response cycle example that shows the problem (some of the output has been omitted):

$ curl -v -X GET -H "Accept: application/xml" http://localhost:8080/sotaro/say/boo
* Connected to localhost (::1) port 8080 (#0)
> GET /sotaro/say/boo HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: application/xml
>
< HTTP/1.1 406 Not Acceptable
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1
< Content-Type: text/html;charset=utf-8
< Content-Language: en
< Content-Length: 1067
< Date: Wed, 04 Jun 2014 12:48:44 GMT
<
<html>
  <body>
    <h1>HTTP Status 406 -</h1>
    <p><b>message</b></p>
    <p><b>description</b> <u>The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.</u></p>
  </body>
</html>

这是玩具示例的模块.

我尝试过的上下文配置之一:

One of the context configs I tried:

<?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:aop="http://www.springframework.org/schema/aop"
  xmlns:context="http://www.springframework.org/schema/context" 
  xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:util="http://www.springframework.org/schema/util" 
  xmlns:mvc="http://www.springframework.org/schema/mvc"
  xmlns:oxm="http://www.springframework.org/schema/oxm"
  xsi:schemaLocation="
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
  http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
  http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-4.0.xsd">

  <context:component-scan base-package="io.github.gv0tch0.sotaro"/>

  <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="favorPathExtension" value="false" />
    <property name="ignoreAcceptHeader" value="false" />
    <property name="useJaf" value="false" />
    <property name="defaultContentType" value="application/xml" />
    <property name="mediaTypes">
      <map>
        <entry key="xml" value="application/xml" />
      </map>
    </property>
  </bean>

  <bean id="xmlConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
    <constructor-arg ref="jaxbMarshaller" />
    <property name="supportedMediaTypes" value="application/xml" />
  </bean>

  <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
    <property name="packagesToScan">
      <list>
        <value>io.github.gv0tch0.sotaro.*</value>
      </list>
    </property>
  </bean>

  <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
    <mvc:message-converters register-defaults="false">
      <ref bean="xmlConverter" />
    </mvc:message-converters>
  </mvc:annotation-driven>

</beans>

控制器:

@Controller
public class Say {
  @RequestMapping(value = "/say/{what}", 
                  produces = {MediaType.APPLICATION_XML_VALUE}, 
                  method = RequestMethod.GET)
  public @ResponseBody SayWhat say(@PathVariable("what") String what) {
    return echo(what);
  }
  private SayWhat echo(String what) {
    SayWhat echo = new SayWhat();
    echo.setWhat(what);
    return echo;
  }
}

响应对象(生成 JAXB2):

Response Object (JAXB2 generated):

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "SayWhat", propOrder = {"what"})
public class SayWhat {
  @XmlElement(required = true)
  protected String what;

  public String getWhat() {
      return what;
  }
  public void setWhat(String value) {
    this.what = value;
  }
}

以及它生成的架构:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:tns="urn:io:github:gv0tch0:sotaro"
           targetNamespace="urn:io:github:gv0tch0:sotaro"
           version="0.0.1">
  <xs:element name="say" type="tns:SayWhat" />
  <xs:complexType name="SayWhat">
    <xs:sequence>
      <xs:element name="what" type="xs:string" minOccurs="1" maxOccurs="1" />
    </xs:sequence>
  </xs:complexType>
</xs:schema>

推荐答案

所以罪魁祸首是响应对象缺少 JAXBElement-wrapper 和配置为支持的 Jaxb2MarshallerJAXBElement 包装的响应.

So the culprit was a missing JAXBElement-wrapper around the response object and a Jaxb2Marshaller configured to support JAXBElement-wrapped responses.

@Controller 变为:

The @Controller becomes:

@Controller
public class Say {
  private final static ObjectFactory JAXB_FACTORY = new ObjectFactory();

  @RequestMapping(value = "/say/{what}", 
                  produces = {MediaType.APPLICATION_XML_VALUE}, 
                  method = RequestMethod.GET)
  public @ResponseBody JAXBElement<SayWhat> say(@PathVariable("what") String what) {
    return echo(what);
  }

  private JAXBElement<SayWhat> echo(String what) {
    SayWhat echo = new SayWhat();
    echo.setWhat(what);
    return JAXB_FACTORY.createSay(echo);
  }
}

并且 Jaxb2Marshaller-部分 spring 配置变为:

And the Jaxb2Marshaller-part of the spring configuration becomes:

<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
  <property name="supportJaxbElementClass" value="true"/>
  <property name="packagesToScan">
    <list>
      <value>io.github.gv0tch0.sotaro</value>
    </list>
  </property>
</bean>

如果 Jaxb2Marshaller 实现发现 JAXB 注释类所在的包包含一个能够在 JAXBElement-s 中包装响应的 ObjectFactory 并且只是使用原始配置开箱即用.

It would have actually been nice if the Jaxb2Marshaller implementation discovered that the package where the JAXB annotated classes are contains an ObjectFactory capable of wrapping responses in JAXBElement-s and just worked out of the box with the original configuration.

这篇关于“406 不可接受"尝试通过 HTTP 为 Spring Web MVC 服务配置 XML 编组时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆