Spring MVC 控制器中 JsonView 的动态选择
Dynamic Selection Of JsonView in Spring MVC Controller
我知道可以使用 @JsonView(...)
注释控制器方法以在 Spring MVC 中静态定义单个视图 class。不幸的是,这意味着对于我可能拥有的每种类型的视图,我都需要一个不同的端点。
我看到其他人也问过这个 before。虽然这种方法可能有效,但 Spring 通常有很多方法可以做同样的事情。有时,如果您对某些内部结构有一些了解,解决方案可能比乍看起来简单得多。
我想要一个控制器端点,它可以动态 select 基于当前主体的适当视图。我是否可以 return 具有包含适当视图 class 或 MappingJacksonValue
实例的属性的 Model
直接?
我在 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
,但我无法解决是否有办法通过 return 包含必要信息的特定值来绕过它Jackson2HttpMessageConverter
选择合适的视图。
非常感谢任何帮助。
万一别人想实现同样的事情,其实很简单。
您可以直接从您的控制器中 return 一个 org.springframework.http.converter.json.MappingJacksonValue
实例,其中包含您要序列化的对象和视图 class。
这将由 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;
}
这是对我有帮助的上述答案的变体。我在使用 Spring HATEOAS 负载时直接 returning MappingJacksonValue
发现了问题。如果我 return 它直接来自控制器的处理程序,由于某种原因 Resources
和 ResourceSupport
mixins 没有得到正确应用并且 JSON HAL _links 被呈现为链接。 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);
}
}
}
我知道可以使用 @JsonView(...)
注释控制器方法以在 Spring MVC 中静态定义单个视图 class。不幸的是,这意味着对于我可能拥有的每种类型的视图,我都需要一个不同的端点。
我看到其他人也问过这个 before。虽然这种方法可能有效,但 Spring 通常有很多方法可以做同样的事情。有时,如果您对某些内部结构有一些了解,解决方案可能比乍看起来简单得多。
我想要一个控制器端点,它可以动态 select 基于当前主体的适当视图。我是否可以 return 具有包含适当视图 class 或 MappingJacksonValue
实例的属性的 Model
直接?
我在 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
,但我无法解决是否有办法通过 return 包含必要信息的特定值来绕过它Jackson2HttpMessageConverter
选择合适的视图。
非常感谢任何帮助。
万一别人想实现同样的事情,其实很简单。
您可以直接从您的控制器中 return 一个 org.springframework.http.converter.json.MappingJacksonValue
实例,其中包含您要序列化的对象和视图 class。
这将由 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;
}
这是对我有帮助的上述答案的变体。我在使用 Spring HATEOAS 负载时直接 returning MappingJacksonValue
发现了问题。如果我 return 它直接来自控制器的处理程序,由于某种原因 Resources
和 ResourceSupport
mixins 没有得到正确应用并且 JSON HAL _links 被呈现为链接。 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);
}
}
}