5

如果我们使用 Spring MVC 开发 REST,它将支持 XML 和 JSON 数据。我在我的 spring 配置 bean 中写了 ContentNegotiationViewResorverapp-servlet.xml

<bean
        class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"
        p:order="1">
        <property name="mediaTypes">
            <map>
                <entry key="xml" value="application/xml" />
                <entry key="json" value="application/json" />
            </map>
        </property>
        <property name="defaultViews">
            <list>
                <bean class="org.springframework.web.servlet.view.xml.MarshallingView">
                    <property name="marshaller">
                        <bean class="org.springframework.oxm.xstream.XStreamMarshaller"
                            p:autodetectAnnotations="true" />
                    </property>
                </bean>
                <bean
                    class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
            </list>
        </property>
    </bean>

我的spring REST控制器是:

@Controller
@RequestMapping("/rest/customers")
class CustomerRestController {

protected Log log = LogFactory.getLog(CustomerRestController.class);

@RequestMapping(method = POST)
@ResponseStatus(CREATED)
public void createCustomer(@RequestBody Customer customer,
        HttpServletResponse response) {

    log.info(">>>" + customer.getName());
    response.setHeader("Location", String.format("/rest/customers/%s",
            customer.getNumber()));
}


@RequestMapping(value = "/{id}", method = GET)
@ResponseBody
public Customer showCustomer(@PathVariable String id) {
    Customer c = new Customer("0001", "teddy", "bean");
    return c;
}


@RequestMapping(value = "/{id}", method = PUT)
@ResponseStatus(OK)
public void updateCustomer(@RequestBody Customer customer) {
    log.info("customer: " + customer.getName());
}

我在我的客户域类中设置@XStreamAlias("customer")了注释。但是当我尝试访问它时http://localhost:8080/rest/customers/teddy.xml,它总是响应 JSON 数据。

我在我的客户域类中设置@XmlRootElement(name="customer")了注释。但是当我尝试访问它时http://localhost:8080/rest/customers/teddy.json,它总是响应 XML 数据。

有什么不对 ?

4

6 回答 6

2

我认为“xml”内容类型应该映射到“text/xml”而不是“application/xml”。此外,要强制基于扩展的内容类型解析器,您可以尝试将“ContentNegotiatingViewResolver”的“favorPathExtension”属性设置为 true(尽管默认情况下它应该是 true!)

编辑:我现在已经在这个 GIT 位置添加了一个工作示例 -git://github.com/bijukunjummen/mvc-samples.git如果您使用 mvn tomcat:run 调出端点,则 json 提供于http://localhost:8080/mvc-samples/rest/customers/teddy.json,xml 提供于http://localhost:8080/mvc-samples/rest/customers/teddy.xml。这使用 JAXB2 而不是 XStream,因为我熟悉 JAXB。我注意到的一件事是,当我的 JAXB 注释在 Customer 类中不正确时,Spring 以您看到的方式提供 JSON 而不是 XML(您可以通过从 Customer 类中删除 XMLRootElement 注释来复制它)注释,我按预期返回了 XML。因此,您的 XStream 配置可能有问题。

编辑2:你是对的!我没有注意到,一旦我取回 xml,我认为 json 现在正在工作。我看到了问题,在AnnotationMethodHandlerAdapter,处理@ResponseBody有点奇怪,它完全忽略了ViewResolvers,并使用注册的MessageConverters,而不是完全绕过ContentNegotiatingViewResolver,现在的一种解决方法是使用@ModelAttribute注释来响应,而不是@ResponseBody,这样查看解析器被调用。现在尝试使用该项目,git@github.com:bijukunjummen/mvc-samples.git看看它是否适合您。这可能是一个 Spring 错误,您可以尝试在 Spring 论坛中提出它并查看他们的建议。

于 2011-01-16T12:54:59.277 回答
2

哪些 Accept 标头会发送到您的服务器?确保您要请求的内容类型在此列表中。

于 2011-01-17T02:11:21.503 回答
1

我有同样的问题。我假设您使用的是 Spring 3 并且您已经使用了<mvc:annotation-driven/>. 我不完全确定,但我认为这会根据 mvc 命名空间配置的消息转换器产生一些冲突。

使用 oxm 命名空间对我有用:

@XmlRootElement(name="person")
class Person {
   private String firstName;
   private String lastName;
}

@Controller 
@RequestMapping("person")
class PersonController {
   @RequestMapping("list")
   public @ResponseBody Person getPerson() {
      Person p = new Person();
      p.setFirstName("hello");
      p.setLastName("world");
      return p;
   }
}

内容配置(mvc 和内部视图解析器在另一个上下文中):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:oxm="http://www.springframework.org/schema/oxm"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

        <oxm:jaxb2-marshaller id="jaxbMarshaller">
        <oxm:class-to-be-bound name="package.Person" />
    </oxm:jaxb2-marshaller>

    <bean
        class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="defaultContentType" value="text/html" />
        <property name="ignoreAcceptHeader" value="true" />
        <property name="favorPathExtension" value="true" />
        <property name="mediaTypes">
            <map>
                <entry key="json" value="application/json" />
                <entry key="xml" value="application/xml" />
            </map>
        </property>
        <property name="defaultViews">
            <list>
                <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
                <bean class="org.springframework.web.servlet.view.xml.MarshallingView">
                    <property name="marshaller" ref="jaxbMarshaller" />
                </bean>
            </list>
        </property>
    </bean>
</beans>

此示例使用 JAXB,因此您需要在类路径中使用 jaxb-api 和 jaxb-impl。

另外,只是提示,您不需要 app-servlet.xml。在您的 web.xml 中,将配置设置为 null 并让 Context Listener 为您加载它们:

<listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/mvc-context.xml, /WEB-INF/spring/content-negotiation-context.xml</param-value>
    </context-param>
    <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value/>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
于 2011-01-16T16:39:36.327 回答
1

好吧,我有一个解决方案,但我不知道这是否是您向客户展示的正确方法:

@RequestMapping(value = "/{id}", method = GET)
@ResponseBody
public Customer showCustomer(@PathVariable String id) {
    Customer c = new Customer("0001", "teddy", "bean");
    return c;
}

在这一部分中,我们使用 Spring 的 MVC,并且在控制器中我们应该返回一个视图,所以我删除了注释@ResponseBody并返回String带有视图名称的 a,因为在我们的 XML 中我们添加了 aContentNegotiatingViewResolver并且当我们有ResponseBodycontentnegociationviewresolver时忽略,因为正在等待视图,但我们返回了对象,所以方法应该是这样的:

@RequestMapping(value = "/{id}", method = GET)

public String showCustomer(@PathVariable String id, ModelMap model) {
     Customer c = new Customer("0001", "teddy", "bean");
     model.addAttribute("customer",c);
    return "myView";
}

这对我有用,如果你有问题,你可以添加到你的app-servlet.xml

这个bean,但我认为你不必添加这个。

<bean
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix">
        <value>/WEB-INF/views/</value>
    </property>
    <property name="suffix">
        <value>.jsp</value>
    </property>
</bean>

我从 mkyong.com 得到了答案

于 2012-02-09T07:28:32.117 回答
1

Spring 3.1 使用注释produces上的新元素解决了您提到的问题。@RequestMapping这允许您控制HttpMessageConverterSpring 应用于您的对象。

我写了一篇关于它的博客文章:

http://springinpractice.com/2012/02/22/supporting-xml-and-json-web-service-endpoints-in-spring-3-1-using-responsebody/

于 2012-02-23T05:37:30.557 回答
0

使用浏览器访问控制器将发送一个典型的浏览器 Accept 标头。它不会匹配任何视图解析器并默认为第一个 (application/xml) 或匹配,因为 application/xml 在接受列表中。

我可以推荐使用 RestClient http://code.google.com/p/rest-client/来完全控制您想要发送的 Accept 标头(如果有的话)。

我不建议使用 text/xml,因为默认字符集是 US-ASCII 而不是 UTF-8。这可能会在未来产生时髦的编码问题。您始终可以指定编码,但 appliation/xml 具有 UTF-8 默认编码。

于 2011-02-23T19:28:55.530 回答