【问题标题】:Weird Spring MVC 4.2.x behaviour on controller method with ResponseBody and returning Model class instance带有 ResponseBody 和返回 Model 类实例的控制器方法上的奇怪 Spring MVC 4.2.x 行为
【发布时间】:2017-06-08 15:46:27
【问题描述】:

我有一些旧版控制器将一些数据放入Model 对象(Thymeleaf 模板需要)。

现在我必须在 REST 服务中返回与 JSON 相同的数据。

出于这些目的,我将数据准备块包装到单独的方法中,以便在两个地方使用:用于 thymeleaf 模板的旧方法和新方法:

@RequestMapping(value = "/index", method = RequestMethod.GET)
public String index(Model model) {
    prepareIndexModel(model);
    return "index";
}

@RequestMapping(value = "/index/model", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public Map<String, Object> indexModel(Model model) {
    prepareIndexModel(model);
    return model.asMap();
}

private void prepareIndexModel(Model model) {
    model.addAttribute("prop1", ...);
    ...
}

但是,当我尝试通过 GET /index/model 访问时,我收到以下错误:

org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "/WEB-INF/templates/index/model.html")

所以它只是认为我的方法不是 REST 方法。我猜,这是因为方法实际上返回了实现两个接口的ExtendedModelMap 类的实例:ModelMap

因此,将/index/model 方法更改为:

@RequestMapping(value = "/index/model", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public Map<String, Object> indexModel(Model model) {
    prepareIndexModel(model);
    return new LinkedHashMap<>(model.asMap());
}

一切都按预期开始:GET /index/model 返回我想要的 JSON。所以基本上我只是把模型包装成LinkedHashMap

我的问题是这是一个特定的行为还是只是一个错误?我希望通过使用@ResponseBody 注释来注释方法, Spring 应该忽略返回对象是Model 的实例这一事实。不应该吗?

更新:@Sotirios Delimanolis 提供了指向非常相似的question 的链接,该链接大约是 3.2 版。但我的问题不是关于为什么,而是 Spring 的 bug 还是它在文档中的某个位置指定?

UPDATE-2:还请注意,在链接的问题中,方法具有Model 返回类型,并且描述了它的行为。就我而言,我有 Map&lt;...&gt; 返回类型,我认为是什么让这种行为变得奇怪且不一致!

【问题讨论】:

  • 链接的副本是在 Spring 仍在 3.x 上的时候。答案仍然适用。 getDefaultReturnValueHandlers 以相同的顺序注册或多或少相同的HandlerMethodReturnValueHandlers,即。 Model 的一个在 @ResponseBody 的一个之前。此外,asMap 返回一个同样实现了ModelMap
  • 1) 我使用的是 Spring 4。 2) 你仔细阅读我的问题了吗?我已经明确强调了model.asMap() 返回的对象,其类也实现了Model
  • 正如我在副本中的回答所解释的那样,处理 ModelModelMethodProcessor 在处理 @ResponseBodyRequestResponseBodyMethodProcessor 之前起作用。
  • 嗯,考虑一下。
  • 在我看来,这是supportsReturnType 的文档中的一个错误,而不是您目睹的行为。

标签: java spring spring-mvc model-view-controller


【解决方案1】:

这是一种特定的行为还是仅仅是一个错误?

我会说这是 undefined 行为。

Spring Framework Reference 定义返回类型Model 表示带有模型的视图。它还定义了Map 的返回类型表示以地图为模型的视图。这些都是明确定义的。

它还指定对于带有@ResponseBody注解的方法,返回值写入响应HTTP正文。同样,这是明确定义的。

它没有指定的是,当@ResponseBody 返回ModelMap 时会发生什么。

返回 Map 以编码为 JSON 是很常见的,并且可以正常工作,即 @ResponseBody 优先于返回类型 Map。这是有道理的。

但是,Model 专门用于 View 的模型属性,因此返回类型 Model 优先于 @ResponseBody 的事实是有道理的。但正如我所说,这不是 指定的 行为,但也不是错误。它是未定义

如果你问我它应该做什么,我会留下它undefined,因为Model as @ResponseBody 对我来说没有任何意义.


另外,文档并没有区分声明的返回类型和实际的返回类型,所以使用的是 undefined

实现使用 actual 返回类型,其优点是您的处理程序方法可以返回 Object 然后返回例如ModelAndViewHttpEntity,视情况而定。这种灵活性是有道理的,但据我所知,它并没有明确定义


所以,@ResponseBody 组合的结果,声明返回类型为Map,但实际返回类型为Model,是未定义

如果你问我(你确实做了),我会说 你的 代码有问题。如果您想返回 Map 作为响应正文发送,为什么不自己创建 Map。向 Spring 框架请求视图 Model 对我来说毫无意义。

考虑到使用 Model 和指定 @ResponseBody 的混合信号,即使阅读代码,我也不确定您的实际意图。

结论:不要偷懒,自己创建Map

【讨论】:

    猜你喜欢
    • 2011-08-09
    • 2014-01-23
    • 2015-06-04
    • 2017-01-16
    • 1970-01-01
    • 1970-01-01
    • 2022-12-07
    • 1970-01-01
    • 2016-04-02
    相关资源
    最近更新 更多