35

我知道可以注释控制器方法以@JsonView(...)在 Spring MVC 中静态定义单个视图类。不幸的是,这意味着我需要为我可能拥有的每种类型的视图使用不同的端点。

我看到其他人以前也问这个。虽然这种方法可能有效,但 Spring 通常有很多方法可以做同样的事情。有时,如果您对某些内部结构有一点了解,则解决方案可能比最初看起来要简单得多。

我想要一个可以根据当前主体动态选择适当视图的控制器端点。我是否可以返回一个Model包含适当视图类或MappingJacksonValue直接实例的属性?

我看到org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal有一段代码可以确定要使用的视图:

if (value instanceof MappingJacksonValue) {
            MappingJacksonValue container = (MappingJacksonValue) object;
            value = container.getValue();
            serializationView = container.getSerializationView();
        }

这似乎来自org.springframework.web.servlet.mvc.method.annotation.JsonViewResponseBodyAdvice#beforeBodyWriteInternal但我无法确定是否有一种方法可以绕过它,只需返回一个包含Jackson2HttpMessageConverter选择正确视图所需信息的特定值。

非常感谢任何帮助。

4

2 回答 2

46

如果其他人想要实现同样的目标,这实际上非常简单。

您可以直接org.springframework.http.converter.json.MappingJacksonValue从控制器返回一个实例,其中包含要序列化的对象和视图类。

这将被该方法拾取org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal并使用适当的视图。

它的工作原理是这样的:

@RequestMapping(value = "/accounts/{id}", method = GET, produces = APPLICATION_JSON_VALUE)
public MappingJacksonValue getAccount(@PathVariable("id") String accountId, @AuthenticationPrincipal User user) {
    final Account account = accountService.get(accountId);
    final MappingJacksonValue result = new MappingJacksonValue(account);
    final Class<? extends View> view = accountPermissionsService.getViewForUser(user);
    result.setSerializationView(view);
    return result;
}
于 2015-03-17T15:41:01.860 回答
17

这是上述答案的变体,对我有所帮助。我发现MappingJacksonValue使用 Spring HATEOAS 有效负载时直接返回的问题。如果我直接从控制器的处理程序返回它,由于某种原因,ResourcesResourceSupportmixins 没有正确应用,并且 JSON HAL _links 被呈现为链接。此外,SpringResponseEntity也不会按照它应该在有效负载中显示bodystatus对象的方式呈现。

使用ControllerAdvice来实现相同的帮助,现在我的有效负载被正确渲染,并且视图根据需要应用

@ControllerAdvice(assignableTypes = MyController.class)
public class MyControllerAdvice extends AbstractMappingJacksonResponseBodyAdvice {

  @Override
  protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType, MethodParameter returnType,
                                         ServerHttpRequest req, ServerHttpResponse res) {
    ServletServerHttpRequest request = (ServletServerHttpRequest)req;
    String view = request.getServletRequest().getParameter("view");
    if ("hello".equals(view)) {
      bodyContainer.setSerializationView(HelloView.class);
    }
  }
}
于 2016-10-31T17:42:07.550 回答