Spring mvc Spring MVC控制器中JsonView的动态选择

Spring mvc Spring MVC控制器中JsonView的动态选择,spring-mvc,json-view,Spring Mvc,Json View,我知道可以用@JsonView注释控制器方法。。。在SpringMVC中静态定义单个视图类。不幸的是,这意味着我可能拥有的每种类型的视图都需要不同的端点 我看到其他人问过这个问题。虽然这种方法可能有效,但Spring通常有很多方法来做同样的事情。有时候,如果您对一些内部结构有一点了解,那么解决方案可能会比最初看起来简单得多 我希望有一个控制器端点,可以根据当前主体动态选择适当的视图。我是否可以直接返回一个包含适当视图类或MappingJacksonValue实例的属性的模型 我在org.spri

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

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

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

我在org.springframework.http.converter.json.AbstractJackson2HttpMessageConverterwriteInternal中看到一段代码,它决定了要使用哪个视图:

if (value instanceof MappingJacksonValue) {
            MappingJacksonValue container = (MappingJacksonValue) object;
            value = container.getValue();
            serializationView = container.getSerializationView();
        }
它似乎来自org.springframework.web.servlet.mvc.method.annotation.jsonviewsresponsebodyadviceforebodywriteinternal,但我很难确定是否有一种方法可以绕过它,只需返回一个特定的值,该值包含Jackson2HttpMessageConverter选择正确答案所需的信息视图


非常感谢您的帮助。

如果有人希望实现同样的目标,其实很简单

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

这将由org.springframework.http.converter.json.AbstractJackson2HttpMessageConverterwriteInternal方法获取,并将使用适当的视图

它的工作原理如下:

@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;
}

下面是上述答案的一个变体,它帮助了我。我发现在使用Spring HATEOAS有效载荷时直接返回MappingJacksonValue时出现问题。如果我直接从控制器的处理程序返回它,由于某些原因,资源和ResourceSupport混合没有正确应用,JSON HAL_链接被呈现为链接。此外,Spring ResponseEntity不会呈现,因为它应该显示有效负载中的body和status对象

使用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);
    }
  }
}

您还可以配置ContentNegotingViewResolveRas作为旁注,我已经决定,在某些情况下,这样做可能是一个坏主意或指示,或者是一些糟糕的设计。考虑到在某些情况下,端点可能产生缓存控制头,或者如果您想记录API响应,并且一些JSON属性对所使用的视图有条件,那么它可以使生命更加复杂,甚至可能暴露出不应该在例如缓存控制处理不善的情况下的细节。我现在的观点是,如果调用方使用Accept头,那么作为单独的URI或不同的内容类型,这些可能会更好。