Cosa sono i SOAPHeader e a cosa servono ?
Negli articoli a proposito della specifica SOAP (Web Services: Technology Overview) abbiamo visto che i messaggi SOAP possono presentare una sezione di Header, appositamente pensata per contenere informazioni legate ai protocolli infrastrutturali, mentre nei SOAP Body possiamo inserire informazioni legate ai protocolli applicativi.
Cosa sono i protocolli applicativi ed i protocolli infrastrutturali ?
I protocolli applicativi sono quelli specifici per una particolare applicazione. Sono disegnati e progettati generalmente da una o due realtà al massimo e sono funzionali allo scopo preciso per cui vengono elaborati. Sono in pratica dei protocolli definiti ad hoc per un determinato scopo da raggiungere.
Per esempio quando qualche anno fa si concordavano dei protocolli di dialogo tra diversi apparati, via seriale per esempio, i protocolli erano orientati al 100% allo scopo da raggiungere e si dicevano "protocolli applicativi".
I protocolli infrastrutturali invece prescindono dall'applicazione che li deve utilizzare e si concentrano sulla tecnologia. Di solito sono applicabili a diversi protocolli applicativi senza alcuna variazione e ne completano/arricchiscono le potenzialità e le funzionalità. Pensiamo ad un protocollo di autenticazione. Avrebbe senso "contaminare" ogni funzione applicativa con delle informazioni di autenticazione ? Oppure ha più senso elaborare un protocollo infrastrutturale di autenticazione, da applicare a qualsiasi protocollo applicativo che richieda l'autenticazione dell'utente ?
I SOAP Header permettono di definire dei protocolli infrastrutturali da applicare ai nostri messaggi SOAP. Un classico esempio di SOAP Header è proprio un header di autenticazione di un utente che, a prescindere dal Web Method che sta invocando, vuole farsi "riconoscere" tramite un meccanismo standard. Vediamo un esempio di SOAP Header di autenticazione "fatto in casa":
<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <SOAPSecurityHeader xmlns="http://www.devleap.it/NS/wsWithHeader"> <UserName>Paolo</UserName> <UserPassword>Pippo</UserPassword> </SOAPSecurityHeader> </soap:Header> <soap:Body> <ExecTransaction xmlns="http://www.devleap.it/NS/wsWithHeader"> <FromAccount>PP52145</FromAccount> <ToAccount>SC43526</ToAccount> <EuroAmount>190.70</EuroAmount> </ExecTransaction> </soap:Body> </soap:Envelope>
Come si può vedere, all'interno della sezione soap:Header è presente un tag custom che contiene al suo interno una userid e una password. Se informo chi deve invocare il mio servizio del fatto che il mio meccanismo di security prevede, nel Header SOAP, il passaggio di queste informazioni, posso poi utilizzare questa tecnica di autenticazione in qualsiasi Web Method, a prescindere da quello che sarà l'oggetto della richista SOAP, cioè appunto la parte di protocollo applicativo.
Aggiungere dei SOAPHeader ai Web Services
Vediamo allora innanzitutto in .NET come aggiungere ad un Web Service un SOAP Header. Nel paragrafo successivo vedremo come utilizzarne le informazioni nel Web Method lato server.
Per prima cosa occorre definire una classe che erediti dalla classe base System.Web.Services.Protocols.SOAPHeader :
public class SOAPSecurityHeader: SoapHeaderQuindi dobbiamo definire una variabile public all'interno del Web Service che deve utilizzare l'header, in modo tale da consentire poi ai client di popolare la sezione di header. Infine dobbiamo appoggiarci all'attributo System.Web.Services.Protocols.SOAPHeaderAttribute per assegnare ai Web Method che ne abbiano l'esigenza l'header stesso.
{
public String UserName;
public String UserPassword;
}
[WebService(Namespace="http://www.devleap.it/NS/wsWithHeader")]
public class wsSecuredWithHeader : System.Web.Services.WebService
{
public SOAPSecurityHeader SecurityHeader;
[WebMethod(), SoapHeader("SecurityHeader")]
public Boolean ExecTransactionSecurityRequired(String FromAccount, String ToAccount, Decimal EuroAmount)
{
return(true);
}
}
Attenzione che come argomento dell'attributo SOAPHeaderAttribute non dobbiamo utilizzare il nome del tipo cui corrisponde il SOAP Header, ma il nome della variabile pubblica definita nella classe che rappresenta il Web Service.
Proviamo a visualizzzare nel browser il servizio appena predisposto con la solita pagina di autodocumentazione, vedremo anche le informazioni sulla sezione SOAP Header da fornire. Inoltre non potremo più provare il Web Method derittamente dal browser in quanto non avremmo modo di fornire l'header, che di default è obbligatorio. (http://devlab.devleap.it/PaoloPi/wsSoapHeaders/wsSecuredWithHeader.asmx) Possiamo anche configurare il comportamento del nostro SOAP Header, tramite alcune proprietà interessanti dell'attributo SOAPHeaderAttribute:
- Direction: indica chi deve fornire/ricevere l'header. Può valere SoapHeaderDirection.In/.Out/.InOut .
- MemberName: corrisponde appunto al nome della proprietà pubblica che rappresenta il SOAP Header all'interno del Web Service.
- Required: indica se il SOAP Header deve essere fornito obbligatoriamente o meno.
Nell'esempio precedente infatti abbiamo tre diverse versioni di Web Method con diversi comportamenti del SOAP Header. Vi consiglio di dare uno sguardo ai modelli di richiesta e di risposta che vengono proposti per rendervi conto di che cosa cambia nei vari casi:
[WebMethod(), SoapHeader("SecurityHeader", Required=false)]
public Boolean ExecTransactionSecurityOptional(String FromAccount,
String ToAccount, Decimal EuroAmount)
{
return(true);
}
[WebMethod(), SoapHeader("SecurityHeader", Required=true)]
public Boolean ExecTransactionSecurityRequired(String FromAccount,
String ToAccount, Decimal EuroAmount)
{
return(true);
}
[WebMethod(), SoapHeader("SecurityHeader",Direction=SoapHeaderDirection.InOut, Required=true)]
public Boolean ExecTransactionSecurityInputOutput(String FromAccount,
String ToAccount, Decimal EuroAmount)
{
return(true);
}
(http://devlab.devleap.it/PaoloPi/wsSoapHeaders/wsSecuredWithHeader.asmx)
Vediamo ora come utilizzare dal codice dei Web Method le informazioni presenti all'interno del SOAP Header. E' molto semplice, basta fare riferimento alla proprietà pubblica definita da codice e leggerne il contenuto:
[WebMethod(), SoapHeader("SecurityHeader", Required=true)]
public Boolean ExecTransactionSecurityRequired(String FromAccount,
String ToAccount, Decimal EuroAmount)
{
if ((SecurityHeader.UserName == "Paolo") &&
(SecurityHeader.UserPassword == "Pippo"))
return(true);
else
return(false);
}
Parleremo nei prossimi articoli di come si sviluppano i client .NET di Web Services, ha però senso vedere da subito come sia possibile utilizzare i SOAP Header dal codice di un client.
Assumendo di aver già costruito la classe proxy del Web Service (tramite Visual Studio .NET o tramite WSDL.EXE), il client avrà a disposizione nell'istanza di classe proxy una proprietà il cui nome corrisponderà al nome del SOAP Header con l'aggiunta di value in fondo al nome. Sarà sufficiente valorizzare quella proprietà con una istanza del SOAP Header. Anche la classe che definisce il SOAP Header infatti sarà ricostruita sul client insieme alla classe proxy verso il servizio. Vediamo un esempio di codice client in una pagina ASPX che utilizza delle TextBox e delle Label:
private void cmdSend_Click(object sender, System.EventArgs e) { DevLeap.WebServices.wsSecuredWithHeader oWs = new DevLeap.WebServices.wsSecuredWithHeader(); DevLeap.WebServices.SOAPSecurityHeader secHeader = new DevLeap.WebServices.SOAPSecurityHeader(); secHeader.UserName = txtUserName.Text; secHeader.UserPassword = txtUserPassword.Text; oWs.SOAPSecurityHeaderValue = secHeader; lblResult.Text = oWs.ExecTransactionSecurityRequired(txtFromAccount.Text, txtToAccount.Text, Decimal.Parse(txtEuroAmount.Text)).ToString(); }Come si vede creiamo un'istanza della classe proxy e assegnamo alla sua proprietà SOAPSecurityHeaderValue una istanza di una classe SOAPSecurityHeader. Di questa istanza di SOAPSecurityHeader configuriamo le proprietà UserName e UserPassword. La pagina è visibile alla URL http://devlab.devleap.it/PaoloPi/wsSoapHeaders/Client.aspx.
I SOAP Header sono spesso comodi per completare le funzionalità infrastrutturali dei nostri Web Service. Specifiche come WS-Security, WS-Routing, WS-Referral (tutte facenti parte della più ampia specifica GXA - Global XML Web Services Architecture) si basano pesantemente sui SOAP Header.
Nel prossimo ciclo di articoli vedremo come lavorare con .NET per sviluppare client di Web Service sviluppati sia con .NET che con Java.