2013-12-16 6 views
7

Sto provando a creare un controller MVC Spring che riceverà un modulo POST con un parametro in formato JSON e Spring lo convertirà automaticamente in un oggetto Java.Conversione automatica del parametro del modulo JSON in Spring MVC 4.0

  • Tipo di richiesta contenuto è application/x-www-form-urlencoded
  • Il nome del parametro che contiene una stringa JSON è data.json

Questo è il controller:

@Controller 
public class MyController { 
    @RequestMapping(value = "/formHandler", method = RequestMethod.POST) 
    public @ResponseBody String handleSubscription(
     @RequestParam("data.json") MyMessage msg) { 
     logger.debug("id: " + msg.getId()); 
     return "OK"; 
    } 
} 

E questo è ciò che il MyMessage oggetto appare come:

public class MyMessage { 
    private String id; 
    // Getter/setter omitted for brevity 
} 

Forse non a caso, la pubblicazione di un modulo con il parametro data.json = { "id": "Ciao"} risultati in errore HTTP 500 con questa eccezione:

org.springframework.beans.ConversionNotSupportedException: 
    Failed to convert value of type 'java.lang.String' to required type 'MyMessage' 
nested exception is java.lang.IllegalStateException: 
    Cannot convert value of type [java.lang.String] to required type [MyMessage]: no matching editors or conversion strategy found 

Se ho letto il MappingJackson2HttpMessageConverter docs correttamente, Jackson JSON la conversione è innescato da Content-Type application/json, che io ovviamente non posso usare dal momento che questa è una forma POST (e io non controllo la parte di invio).

E 'possibile ottenere Spring per convertire la stringa JSON in un'istanza di MyMessage, o dovrei semplicemente rinunciare, leggerlo come una stringa ed eseguire la conversione da solo?

+0

_e io non controllo il distacco part_ vostro cliente deve seguire le specifiche del server, non il contrario. –

+0

Il client è un meccanismo di webhook di terze parti di cui non ho alcun controllo. Ma la tua affermazione mi incuriosisce, c'è qualcosa nelle specifiche HTTP che proibisce l'invio di dati JSON usando un campo modulo/parametro post? –

+0

No, non c'è, ma con REST è diventato convenzionale averlo nel corpo. –

risposta

14

Primavera invoca tuoi @RequestMapping metodi con la riflessione. Per risolvere ogni argomento passerà al richiamo, utilizza le implementazioni di HandlerMethodArgumentResolver. Per @RequestParam parametri annotati, utilizza RequestParamMethodArgumentResolver. Questa implementazione lega un parametro di richiesta a un singolo oggetto, tipicamente un tipo String o qualche Number.

Tuttavia, il caso d'uso è un po 'più rara. Raramente si riceve json come parametro di richiesta, che è il motivo per cui penso che si dovrebbe ripensare il vostro disegno, ma se non avete altra scelta, è necessario registrare un personalizzato PropertyEditor che si prenderà cura di convertire il valore del parametro di richiesta json nel tuo tipo personalizzato.

registrazione è semplice in un metodo annotato @InitBinder nella @Controller classe

@InitBinder 
public void initBinder(WebDataBinder dataBinder) { 
    dataBinder.registerCustomEditor(MyMessage.class, new PropertyEditorSupport() { 
     Object value; 
     @Override 
     public Object getValue() { 
      return value; 
     } 

     @Override 
     public void setAsText(String text) throws IllegalArgumentException { 
      value = new Gson().fromJson((String) text, MyMessage.class); 
     } 
    }); 
} 

In questo caso particolare, non abbiamo bisogno di tutti i metodi dell'interfaccia PropertyEditor, in modo che possiamo usare PropertyEditorSupport che è un utile implementazione predefinita di PropertyEditor. Semplicemente implementiamo i due metodi che ci interessano utilizzando il sapore del parser JSON che vogliamo. Ho usato Gson perché era disponibile.

Quando Spring vede che ha un parametro di richiesta richiesto, controlla il tipo di parametro, trova il tipo MyMessage e cerca un PropertyEditor registrato per quel tipo. Lo troverà perché l'abbiamo registrato e lo userà per convertire il valore.

Potrebbe essere necessario implementare altri metodi di PropertyEditor a seconda di ciò che si farà dopo.

La mia raccomandazione è di non inviare mai JSON come parametro di richiesta. Impostare il tipo di contenuto della richiesta su application/json e inviare lo json come corpo della richiesta. Quindi utilizzare @RequestBody per analizzarlo.

+2

"La mia raccomandazione è di non inviare mai JSON come parametro di richiesta". Sono d'accordo, ma non sto controllando il lato mittente, sto solo implementando un ricevitore per un webhook di terze parti. –

+0

È per Mandrill? Immagino che avessero un cliente che poteva ricevere solo i parametri del modulo invece del corpo del contenuto. –

+0

@NickSpacek No, è un meccanismo per ricevere chiamate [Unbounce webhook] (http://support.unbounce.com/intries/307685-how-does-the-form-webhook-work). –

4

È inoltre possibile utilizzare @RequestPart come questo:

@RequestMapping(value = "/issues", method = RequestMethod.POST, headers = "Content-Type=multipart/form-data") 
public String uploadIssue(@RequestParam("image") MultipartFile file, @RequestPart("issue") MyMessage issue) 
+0

Questa dovrebbe essere la risposta accettata – dcg

Problemi correlati