spring-mvc - mapperfeature - spring mvc jackson example
Selección dinámica de JsonView en Spring MVC Controller (2)
Soy consciente de que es posible anotar los métodos del controlador con @JsonView(...)
para definir estáticamente una clase de vista única en Spring MVC. Desafortunadamente, esto significa que necesito un punto final diferente para cada tipo de vista que pueda tener.
Veo que otras personas han preguntado esto before . Si bien este enfoque puede funcionar, Spring a menudo tiene muchas formas de hacer lo mismo. A veces, la solución puede ser mucho más simple de lo que parece al principio si solo tiene un poco de conocimiento acerca de algunos de los aspectos internos.
Me gustaría tener un único punto final de controlador que pueda seleccionar dinámicamente la vista apropiada según el principal actual. ¿Es posible que devuelva un Model
con un atributo que contenga la clase de vista apropiada o tal vez una instancia de MappingJacksonValue
directamente?
Veo en org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal
hay un fragmento de código que determina qué vista usar:
if (value instanceof MappingJacksonValue) {
MappingJacksonValue container = (MappingJacksonValue) object;
value = container.getValue();
serializationView = container.getSerializationView();
}
Lo que parece provenir de org.springframework.web.servlet.mvc.method.annotation.JsonViewResponseBodyAdvice#beforeBodyWriteInternal
pero tengo problemas para resolver si hay una manera de evitarlo simplemente devolviendo un valor particular que contenga lo necesario información para el Jackson2HttpMessageConverter
para elegir la vista correcta.
Cualquier ayuda muy apreciada.
Aquí hay una variación de la respuesta anterior que me ayudó. Encontré problemas al devolver MappingJacksonValue
directamente al usar las cargas útiles Spring HATEOAS. Si lo devuelvo directamente desde el controlador del controlador, por alguna razón, los mixins Resources
y ResourceSupport
no se aplican correctamente y los enlaces JSON HAL se representan como enlaces. Además, Spring ResponseEntity
no se procesa, ya que debería mostrar los objetos de body
y status
en la carga útil.
Usar ControllerAdvice
para lograr lo mismo ayudó a eso y ahora mis cargas útiles se representan correctamente y las vistas se aplican según sea necesario
@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);
}
}
}
En caso de que alguien más quiera lograr lo mismo, en realidad es muy simple.
Puede devolver directamente una instancia org.springframework.http.converter.json.MappingJacksonValue
desde su controlador que contiene tanto el objeto que desea serializar como la clase de vista.
Esto será recogido por el método org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal
y se org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal
la vista correspondiente.
Funciona algo como esto:
@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;
}