2013-05-10 6 views
6

Avere una molla MVC web app Java, e sto facendo una richiesta jquery ajax posta. Il mio controller è configurato per ricevere e inviare dati json. Tutto funziona, la stringa JSON è ben formattata e il Controller può creare e popolare un oggetto Command e popolarlo con il contenuto dei dati della richiesta JSON. Tuttavia, sto aggiornando i dati per un oggetto Contatto e il mio elemento modulo JSP contiene solo un sottoinsieme di tutti i dati richiesti per l'aggiornamento del DB. Nella mia richiesta GET iniziale per la pagina JSP con il modulo richiamo tutti i dati necessari dal DB, popola un oggetto Command Command e quindi lego tale oggetto comando al modello.Primavera richiesta 3 AJAX POST con @RequestBody e @ModelAttribute e @SessionAttribute utilizzato insieme?

Se si eseguisse un normale invio di moduli di invio POST, credo che dichiarare il mio oggetto comando come @SessionAttribute e fare riferimento a tale oggetto Command utilizzando @ModelAttribute nel mio metodo onSubmit() POST sarebbe sufficiente. Spring recuperava l'oggetto comando già popolato dalla mia sessione e quindi associava (sovrascrive) quei valori che sono cambiati a seguito della richiesta POST. Questo oggetto comando aggiornato potrebbe quindi essere utilizzato come parametro per un aggiornamento DB.

Tuttavia, io sto usando molla 3 e sfruttando @RequestBody tipo paramater. Non riesco a convincere Spring a darmi l'oggetto della sessione e a collegare automaticamente i nuovi valori della richiesta. Mi dà solo il vecchio oggetto comando sessione (senza applicare le modifiche) o un nuovo oggetto comando con solo i valori della richiesta POST.

Ecco un po 'di codice - non funziona:

@SessionAttributes("contactCommand") 
@Controller 
public class ContactController { 


    @RequestMapping(value = "/editContact", method=RequestMethod.GET) 
public String init(ModelMap model, Locale locale, HttpServletRequest request, HttpServletResponse response) throws GeneralException { 
    final ContactCommand cmd = new ContactCommand(); 
    // populate with data from DB etc 
    model.addAttribute("contactCommand", cmd); 
    // etc 
} 

@RequestMapping(value="/editContact",method=RequestMethod.POST, consumes = "application/json", produces = "application/json") 
public @ResponseBody Map<String, ? extends Object> editContactInfo(@RequestBody @ModelAttribute("contactCommand") ContactCommand cmd, HttpServletRequest request, HttpServletResponse response) throws GeneralException { 

// do business logic with command object here 

} 

Qualcuno può per favore mi dica qual è il modo "standard" o "facile" da usare con i dati @RequestBody richiesta JSON e fare That Bind ad un oggetto Command/@ModelAttribute popolato esistente in modo che l'oggetto Command stato costituito, con dati vecchi e nuovi (nello stesso modo in cui viene realizzato facilmente utilizzando un POST http pieno presentare).

Una questione correlata è ciò che è sbagliato con il codice di cui sopra? È possibile utilizzare insieme @SessionAttribute e @RequestBody con contenuto JSON? Se è così, per favore spiega come! Grazie mille per qualsiasi input.

Il mio lavoro è quello di lasciare in giro Primavera creare il nuovo oggetto Command e compilato automaticamente con i dati del modulo. Quindi effettuare una chiamata/recuperare manualmente dalla sessione il vecchio oggetto comando, infine copiare manualmente tutti quegli attributi che non erano presenti nell'invio del modulo nel nuovo oggetto comando. Ora ho tutti i dati necessari insieme in un oggetto comando per applicare il mio aggiornamento SQL con. Ci deve essere un modo più semplice ....;)

UPDATE:

trovato questo post SOF oggi mentre la ricerca ulteriormente questo problema:

Spring Partial Update Object Data Binding

sembra che ci sia alcuna soluzione nota PRIMAVERA fuori dagli schemi ma molto richiesta per conoscere il modo migliore per gestirlo. Nel mio caso, sì, sto usando oggetti di dominio nidificati quindi la soluzione alternativa proposta nel post non è buona. Qualcuno ha qualche altra idea? Per essere chiari, desidero inviare i dati in formato POST JSON al controller (non semplicemente i dati del modulo HTTP post).

Ok, ho aperto una richiesta di primavera Fonte JIRA per questo, forse si tratta di un miglioramento molto bisogno:

https://jira.springsource.org/browse/SPR-10552

Oppure, si tratta di un caso di sfruttare le funzionalità di conversione in Jackson modi intelligenti che sembrano un sacco di tubature.

risposta

0

Perché si desidera annotare ModelAttribute con @RequestBody, è sufficiente disporre di @SessionAttribute e fare riferimento a tale oggetto Command che utilizza @ModelAttribute in caso di JSON.

Qual è la vostra motivazione che sta dietro all'uso @RequestBody

Vedi http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html e

http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html

+0

Grazie per la risposta. Pensavo che per sfruttare l'auto json per la conversione degli oggetti fornita da Jackson su classpath era prassi normale utilizzare RequestBody. Per favore, puoi indicarmi qualsiasi documentazione che indichi che ModelAttribute sia sufficiente per gestire anche JSON? Ho letto quanto segue su un altro post SOF: "ModelAttribute è associato ai parametri del modulo post e RequestBody passa il corpo direttamente al convertitore Json": http://stackoverflow.com/questions/13229584/requestbody-or-modelattribute-with-springrest -servizi web – arcseldon

1

Questa non è una risposta completa, ma spero che vi punto nella giusta direzione.

Di seguito è riportata una classe che viene utilizzata per eseguire il deep binding da JSON a un oggetto esistente utilizzando Jackson. Questo è adattato da un bug report per Jackson qui: https://jira.springsource.org/browse/SPR-10552

public class JsonBinder 
{ 
    private ObjectMapper objectMapper; 

    public JsonBinder(ObjectMapper objectMapper) 
    { 
     super(); 
     this.objectMapper = checkNotNull(objectMapper); 
    } 

    public void bind(Object objToBindInto, InputStream jsonStream) throws JsonProcessingException, IOException 
    { 
     JsonNode root = objectMapper.readTree(checkNotNull(jsonStream)); 
     applyRecursively(checkNotNull(objToBindInto), root); 
    } 

    private void applyRecursively(Object objToBindInto, JsonNode node) throws JsonProcessingException, IOException 
    { 
     PropertyAccessor propAccessor = null; 

     for(Iterator<Entry<String, JsonNode>> i = node.fields(); i.hasNext();) 
     { 
      Entry<String, JsonNode> fieldEntry = i.next(); 
      JsonNode child = fieldEntry.getValue(); 
      if(child.isArray()) 
      { 
       // We ignore arrays so they get instantiated fresh every time 
       // root.remove(fieldEntry.getKey()); 
      } 
      else 
      { 
       if(child.isObject()) 
       { 
        if(propAccessor == null) 
        { 
         propAccessor = PropertyAccessorFactory.forDirectFieldAccess(objToBindInto); 
        } 
        Object o2 = propAccessor.getPropertyValue(fieldEntry.getKey()); 
        if(o2 != null) 
        { 

         // Only remove the JsonNode if the object already exists 
         // Otherwise it will be instantiated when the parent gets 
         // deserialized 
         i.remove(); 
         applyRecursively(o2, child); 
        } 
       } 
      } 
     } 
     ObjectReader jsonReader = objectMapper.readerForUpdating(objToBindInto); 
     jsonReader.readValue(node); 
    } 
} 

Usiamo questo insieme ad un'un'implementazione di HandlerMethodArgumentResolver di primavera.

Non usiamo molti framework MVC di Spring. Stiamo solo creando un backend API JSON utilizzando molte parti diverse di Spring. È un buon numero di impianti idraulici per far funzionare tutto, ma ora i nostri controller sono molto semplici.

Purtroppo non posso mostrare tutto il nostro codice, è comunque abbastanza lungo. Spero che questo risolva almeno parte del problema.