2015-03-05 5 views
18

Sono consapevole che è possibile annotare i metodi del controller con @JsonView(...) per definire staticamente una singola classe di visualizzazione in Spring MVC. Sfortunatamente ciò significa che ho bisogno di un endpoint diverso per ogni tipo di vista che potrei avere.Selezione dinamica di JsonView in Spring Controller MVC

Vedo altre persone hanno chiesto questo before. Mentre questo approccio può funzionare, Spring ha spesso molti modi di fare la stessa cosa. A volte la soluzione può essere molto più semplice di quanto non appaia se hai solo un po 'di conoscenza di alcuni interni.

Mi piacerebbe avere un singolo endpoint del controller che possa selezionare dinamicamente la vista appropriata in base al principale corrente. Posso restituire un Model con un attributo che contiene la classe di visualizzazione appropriata o forse un'istanza MappingJacksonValue direttamente?

vedo in org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal c'è un frammento di codice che determina ciò che vista da usare:

if (value instanceof MappingJacksonValue) { 
      MappingJacksonValue container = (MappingJacksonValue) object; 
      value = container.getValue(); 
      serializationView = container.getSerializationView(); 
     } 

che sembra venire da org.springframework.web.servlet.mvc.method.annotation.JsonViewResponseBodyAdvice#beforeBodyWriteInternal ma sto avendo problemi di lavoro se c'è un modo ho potuto bypass questo semplicemente semplicemente restituendo un valore particolare che contiene le informazioni necessarie allo Jackson2HttpMessageConverter per selezionare la vista corretta.

Qualsiasi aiuto molto apprezzato.

+0

È inoltre possibile configurare un ContentNegotiatingViewResolver – Richard

risposta

28

Nel caso in cui qualcun altro voglia ottenere la stessa cosa, in realtà è molto semplice.

È possibile restituire direttamente un'istanza org.springframework.http.converter.json.MappingJacksonValue dal controller che contiene sia l'oggetto che si desidera serializzare sia la classe di visualizzazione.

Questo verrà prelevato dal metodo org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal e verrà utilizzata la vista appropriata.

Esso funziona in questo modo:

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

Ecco una variazione della risposta di cui sopra, che mi ha aiutato. Ho trovato problemi a restituire MappingJacksonValue direttamente durante l'utilizzo dei payload di Spring HATEOAS. Se lo restituisco direttamente dal gestore del controller, per qualche motivo i mixaggi Resources e ResourceSupport non vengono applicati correttamente e JSON HAL _links viene visualizzato come collegamenti. Anche Spring ResponseEntity non viene visualizzato come dovrebbe mostrare gli oggetti body e status nel payload.

Utilizzando ControllerAdvice per ottenere lo stesso ha aiutato con quello e ora i miei carichi utili sono resi correttamente e le viste vengono applicate in base alle esigenze

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