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

时间:2015-03-05 13:01:13

标签: spring-mvc json-view

我知道可以使用@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的必要信息的特定值,我就无法解决问题选择正确的观点。

任何帮助都非常感激。

2 个答案:

答案 0 :(得分:37)

关于其他人想要达到同样目标的机会,实际上非常简单。

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

答案 1 :(得分:9)

以上是上述答案的变体,对我有所帮助。我发现在使用Spring HATEOAS有效负载时直接返回MappingJacksonValue的问题。如果我直接从控制器的处理程序返回它,由于某种原因,ResourcesResourceSupport mixins未正确应用,而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);
    }
  }
}