【问题标题】:Different response entity body based on accept header with Spring WebMVC REST controller基于带有 Spring WebMVC REST 控制器的接受标头的不同响应实体主体
【发布时间】:2012-11-07 15:09:07
【问题描述】:

我有一个带有这样方法签名的控制器

@RequestMapping(value = "/{project:[A-Z0-9_+\\.\\(\\)=\\-]+}", method = RequestMethod.GET)
public ResponseEntity<Object> lookupProject(@PathVariable String project,
        @RequestParam(value = "fields", required = false) String fields,
        @RequestParam(value = "asList", required = false, defaultValue = "false") boolean asList);

我正在为 JSON 返回 Collection&lt;Object&gt;Map&lt;String, Object&gt;。这不适用于 XML。我宁愿返回一个Project 对象。

是否有一种明确的方法来确定将生成哪种内容类型?解决方法是另一种仅生成 XML 并返回 Project 对象的方法。我宁愿避免重复代码。

我在 Spring 3.1.3-RELEASE。

【问题讨论】:

    标签: java spring rest spring-mvc xml-serialization


    【解决方案1】:

    根据 Spring 文档,您的生产参数将根据 Accept 返回内容,即

    @RequestMapping(value="/someUrl/", produces="application/*")
    public Project someControllerMethod(...)
    

    如果方法传入application/xml,则该方法生成xml,如果是application/json,则生成json等。

    您只需要为适当的对象类型设置编组器。

    这里的文档:

    http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#produces()

    【讨论】:

    • Jackon 已默认注册。 JSON 完美无缺。我不打算为我的自定义对象修改 Jackson 转换器。
    • 我最初提出杰克逊的原因是因为您在问题中没有提及一次。如果我理解正确,您正在尝试创建一个接受所有请求然后绑定到特定模型的通用方法?您是否有理由不将这些拆分为单独的方法?
    • 是的,有一个。我确实想重复代码。只需根据协商的内容类型,我想返回适当的对象。
    • 获取 HttpServletRequest 对象,检查内容类型并适当编组。不过,我不建议这样做,这会使测试更加令人头疼并导致代码混淆。
    • 这正是我不想想做的。如果我这样做,我就不需要 Spring 框架。我想没有内置的解决方案。我可能会提出一张票。
    【解决方案2】:

    要生成的内容类型将取决于客户端可接受的类型。已经提到了一种解决方案,使用produces属性。另一种解决方案是使用ContentNegotiatingViewResolver,它可以根据accept header或其他机制解析视图。

    参考文档http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/servlet/view/ContentNegotiatingViewResolver.html

    例如:http://www.mkyong.com/spring-mvc/spring-3-mvc-contentnegotiatingviewresolver-example/

    【讨论】:

    • 我猜,我的问题被误解了。我想知道其中哪些会被实际提取。
    【解决方案3】:

    尽管,我认为 Dardo 的解决方案是最好的,您可以使用 XMLHttpMessageConverter 检查对象类型,如果它是 Project 对象,则进行不同的转换。

    【讨论】:

    • 好吧,我不打算修改内置的 XML 消息转换器,它的工作方式非常好。检查需要在控制器中进行。
    【解决方案4】:

    在 Spring Framework 4.1 中,您可以使用 ResponseBodyAdvice 在写入之前修改从 @ResponseBody 或 ResponseEntity 方法返回的值。

    http://docs.spring.io/spring-framework/docs/4.1.0.RC2/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyAdvice.html

    【讨论】:

      【解决方案5】:

      我最终分成了两种方法。

      公共方法已重命名为 lookupAsType。委托/返回

      String[] fieldsArray = StringUtils.split(fields, ',');
      return lookup(project, fieldsArray, asList, mediaType);
      

      新方法 lookup 与之前的代码相同,但不包含媒体类型的 if 子句。

      if (mediaType.equals(MediaType.APPLICATION_JSON)) {
          body = projectValues;
      } else if (mediaType.equals(MediaType.APPLICATION_XML)) {
          body = new Project(projectValues);
      } else {
          throw new NotImplementedException("Project lookup is not implemented for media type '" + mediaType + "'");
      }
      

      不好的是 MediaType 不是枚举而是丑陋的类。

      【讨论】:

      • 你试过我的答案了吗?您在滥用框架的构造。
      • 我之前已经有produces = {app/json, app/xml},但我没有任何收获,因为我仍然不知道将写入哪种媒体类型。
      • 所写的媒体类型将与接收的内容相匹配,只要你为你的项目对象设置了 JSON 和 XML 的编组器,如果你接收 application/xml 或 application/json xml 或json 将被生成,你不​​必在你的方法中检查它。
      • 好吧,关键是我不希望在请求 JSON 时序列化 Project,而只希望序列化 projectValues
      • 我明白了,我想我终于明白了,如果我偶然发现了一种不同的实现方式,我会告诉你的,但是由于你要返回两种不同类型的对象,所以不确定它是否可行.
      猜你喜欢
      • 1970-01-01
      • 2015-06-23
      • 2017-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多