Spring MVC Controller中JsonView的动态选择

Posted

技术标签:

【中文标题】Spring MVC Controller中JsonView的动态选择【英文标题】:Dynamic Selection Of JsonView in Spring MVC Controller 【发布时间】:2015-05-06 20:38:17 【问题描述】:

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

我看到其他人问过这个before。虽然这种方法可能有效,但 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 选择正确视图所需信息的特定值.

非常感谢任何帮助。

【问题讨论】:

也可以配置一个ContentNegotiatingViewResolver 【参考方案1】:

这是对我有帮助的上述答案的变体。我发现在使用 Spring HATEOAS 有效负载时直接返回 MappingJacksonValue 的问题。如果我直接从控制器的处理程序返回它,由于某种原因,ResourcesResourceSupport 混合没有正确应用,并且 JSON HAL _links 被呈现为链接。此外,Spring ResponseEntity 未呈现,因为它应该在有效负载中显示 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);
    
  

【讨论】:

【参考方案2】:

万一其他人也想达到同样的目的,其实很简单。

您可以直接从控制器返回一个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;

【讨论】:

作为旁注,我已经决定在某些情况下这样做可能是一个坏主意或指示或一些糟糕的设计。考虑到端点在某些情况下可能会产生缓存控制标头,或者如果您想要记录 API 响应并且某些 JSON 属性取决于正在使用的视图,它可能会使生活变得更加复杂,甚至可能会暴露不应该的细节(例如,在缓存控制处理不当的情况下)。我现在的看法是,作为单独的 URI 或调用者使用 Accept 标头的不同内容类型,这些可能会更好。

以上是关于Spring MVC Controller中JsonView的动态选择的主要内容,如果未能解决你的问题,请参考以下文章

spring mvc 中quartz 怎么去定时调用controller

Spring mvc Controller接口

Spring MVC注解Controller源码流程解析---请求匹配中的容错处理

Spring MVC Controller中JsonView的动态选择

Spring MVC学习笔记——Controller

Spring MVC注解Controller源码流程解析--定位HandlerMethod