Consigli vari su come gestire Webservices SOAP disomogenei con ruby Savon
Quasi tutti i webservices moderni offrono delle web API (abbastanza) REST con formato di dati json, purtroppo e per fortuma il mondo è bello perché vario, nel mondo enterprise un'altra tecnologia basata su xml ha preso piede, anche grazie al supporto del framework Microsoft .NET che è stato scelto da molte grandi aziende, ovviamente sto parlando di SOAP.
Fortunatamente si tratta di un sistema di interscambio di dati open che non obbliga chi si deve interfacciare ad usare la stessa tecnologia.
SOAP con ruby e Savon
Lo scopo di questo post non é ripetere quando già offre l'ottima documentazione di savon piuttosto mi focalizzerò ad alcuni hack, tricks, tricks che mi sono state necessarie per accedere alle varie implementazioni "reali" di SOAP.
1. Installazione e primo utilizzo di savon
gem install savon
Accertatevi di avere un indirizzo ?wsdl valido e successivamente vi consiglio di usare la console di ruby IRB:
irb
> require 'savon'
=> true
> client = Savon::Client.new do
> wsdl.document = "http://www.thomas-bayer.com/axis2/services/BLZService?wsdl"
> end
=> #<Savon::Client:0x00000001431808 @original_self=main, @wsdl=#<Savon::Wasabi::Document:0x000000014314c0 @document="http://www.thomas-bayer.com/axis2/services/BLZService?wsdl", @request=#<HTTPI::Request:0x00000001431470>>, @http=#<HTTPI::Request:0x00000001431470>>
> client.wsdl.actions
> client.wsdl.soap_actions
D, [2011-09-01T23:22:18.324564 #25732] DEBUG -- : HTTPI executes HTTP GET using the net_http adapter
=> [:get_bank]
Le azioni SOAP vengono convertite in automatico dallo stile CamelCase a quello underscore.
2. Forzatura dell'endpoint e disabilitazione della verifica HTTPS
Se non siete stati così fortunati anche con il vsotro WS di riferimento provate a forzare l'endpoint o a disabilitare la verifica del certificato SSL (nel caso non sia registrato presso un'authority)
2. SOAP e standard
Innanzitutto soap fa transitare dati in xml ma sta ad ogni implementazione formalizzare la struttura di risposta. Per esempio, in N backend SOAP che ho avuto a che fare ho trovato N modi diversi rappresentare il fatto che:
- un record venga inserito nel db remoto. Ognuno ha il suo modo di rispondere "ok", che in HTTP sarebbe "200"
- l'invio delle credenziali può avvenire come valori di campi o valori di parametri XML.
- come se non bastasse oltre una certa nidificazione di xml, la struttura potrebbe non essere più xml valido (ne parliamo più avanti)
Per esperienza personale lo standard XML SOAP si riassume nelle poche righe sotto:
POST /serviceurl.asmx HTTP/1.1 Host: servicehost.xxxx.de Content-Type: application/soap+xml; charset=utf-8 Content-Length: length<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
..
</soap12:Body>
</soap12:Envelope>
Quello che può avvenire tra i .. può essere qualsiasi cosa, risposte annidate e con namespace custom, risposte positive o negative,..
3. Quando non usare il wrapper Savon
Savon permette di usare le azioni SOAP come se si trattasse di Hash o altre strutture native ruby, questa modalità è molto comoda in un mondo ideale, ma può essere molto difficile da debuggare visto che le specifiche vengono date in xml e in alcuni casi si hanno degli utilizzi bizzarri.
4. Escape di XML in XML
Per ovviare la rigidità dell'xml e rimandare la perenne carenza progettuale, molte implementazioni di SOAP embeddano xml in xml, cioé nonostante voi inviate delle richieste xml corrette queste potranno dare errori fantasiosi e non debuggabili lato client in quanto un determinato campo dentro <soap12:Body> potrebbe ospitare dei campi xml arbitrari con escaping, ma che sono comunque obbligatori.
Esempio di come gestire la situazione
req = client.request :submit_lead dosoap.xml = <<-STR<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"><soap12:Body><SubmitLead xmlns="http://xxx/"><client>user</client><key>secret</key><lead>#{CGI::escapeHTML( @xml_inner )}</lead></SubmitLead></soap12:Body></soap12:Envelope>STRend
Dove @xml_inner è una stringa con l'xml che richiede l'escaping
> CGI::escapeHTML("<tag>ciao,né</tag>")
=> "<tag>ciao,né</tag>"
Quindi la leggibilità dell'xml ne risentirà di molto :P Ma dovrete essere pronti a questo e ben altro per avere l'interoperabilità sul web.