- WebService: chiamare un metodo su
un server da remoto -
|
|||
COSA SERVE PER QUESTO TUTORIAL | |||
Download | Chiedi sul FORUM | Glossario | un server che supporti ASP .Net | ||
Cosa sono, come utilizzare e come funzionano i WebService di .Net | |||
IL WEBSERVICE In cosa consiste e come realizzare un semplice WebService per effettuare query SQL da remoto.
In un
precedente articolo abbiamo visto come effettuare la serializzazione
XML di oggetti, e abbiamo accennato che questo poteva essere utile per
far comunicare tra loro piattaforma diverse e non sulla stessa macchina.
Oggi esploreremo un'altra funzionalità che si basa sulla serializzazione
XML davvero interessante: i WebService. Un WebService non è nient'altro
che una classe che si trova su un server ASP .Net (in un file con
estensione ASMX) dotata di alcuni metodi che possono essere richiamati
in maniera diretta da una macchina in remoto (il client). Per
comprendere meglio facciamo un esempio molto pratico: alcuni servizi di
hosting non permettono di accedere al DBMS da remoto, ovvero ad esempio
nel caso di MySQL impediscono di connettersi sulla porta 3306 tramite
client come SQLYog o Navicat. Proprio per questo, questi ultimi hanno
studiato delle
soluzioni di HTTP Tunneling consistenti in una pagina PHP verso la
quale viene effettuata la richiesta e che restituisce, in un
formato che non ci occuperemo ora di indagare, il risultato della query. Il WebService che svilupperemo
in questo articolo
mira proprio a questo, ma in una maniera più elegante e semplice: non
dovremo infatti inventare alcun formato con cui restituire (e quindi poi
reinterpretare dal lato client) il risultato della query in quanto
sfrutteremo le funzionalità di .Net basate su SOAP. dsMioDataSet = WebService.EseguiQuestaQuery("user", "pass", "SELECT * FROM utenti") Iniziamo con lo scrivere una semplice funzione che data una stringa di connessione, la query SQL e una serie di parametri restituisce un DataSet con i risultati della query. Public Function ExecuteQuery(ByVal ConnectionString As String, ByVal Command As String, _ ByVal Parameters As OdbcParameter()) As DataSet Dim cnt As New OdbcConnection(ConnectionString) Dim adp As New OdbcDataAdapter(Command, cnt) adp.SelectCommand.Parameters.AddRange(Parameters) Dim dsResult As New DataSet adp.Fill(dsResult) Return dsResult End Function La funzione dovrebbe essere abbastanza autoesplicativa, non fa null'altro che ricevere una stringa di connessione, la query da eseguire ed un array di parametri (opzionalmente) e restituisce il DataSet di risultato al chiamante. Ora trasformiamo questo semplice metodo in un WebService, salvandolo in un file con estensione ASMX all'interno di una cartella del server (nel nostro caso webservice.asmx, nella root di IIS): <%@ WebService Language="VB" Class="Querytor" %> Imports System.Web.Services Imports System.Data Imports System.Data.odbc Public Class Querytor <WebMethod()> _ Public Function ExecuteQuery(ByVal ConnectionString As String, ByVal Command As String, _ ByVal Parameters As OdbcParameter()) As DataSet [...] End Function End Class Vediamo in dettaglio le modifiche che abbiamo apportato:
Imponiamo immediatamente qualche restrizione che riguarda i WebMethod: essi non possono restituire o ricevere come parametri oggetti che non supportino la serializzazione XML (come i dizionari e molti altri), quindi in genere ci si dovrà limitare a oggetti dei tipi primitivi (Integer e derivati, String, Single, Double e così via), classi, strutture e array formati da tipi primitivi o anche più complessi purché dispongano della possibilità di essere serializzate in XML (come ad esempio il DataSet, di cui facciamo uso nella nostra funzione). L'APPLICAZIONE LATO CLIENTUna semplice applicazione console che richiama il webservice dopo aver generato il codice necessario con il tool wsdl. A questo punto abbiamo preparato il nostro WebService e possiamo provare ad utilizzarlo dal lato client. Per poter utilizzare un WebService è necessario generare del codice apposito che farà da wrapper verso il WebService. Nel caso compiliate il vostro codice manualmente da linea di comando o comunque non vi serviate di Visual Studio, per generare il codice necessario utilizzare il tool wsdl (che potrete trovare nella SDK) da linea di comando specificando l'URL in cui si trova il WebServices e il linguaggio in cui generare il codice. Se il WebService si trova in "http://localhost/webservice.asmx" e si vuole generare codice per Visual Basic sotto il namespace MioWebService utilizzare il seguente comando: wsdl http://localhost/webservice.asmx /language:VB /namespace:MioWebService Per ulteriori informazioni digitare "wsdl -?". Se invece si utilizza Visual Studio basta aggiungere una Web Reference al progetto. L'esempio seguente considera una semplice applicazione console che esegue la query invocando il WebService e stampa il DataSet in formato XML: Module RemoteQuery Sub Main() Dim qry As New MioWebService.Querytor qry.Url = "http://localhost/webservice.asmx" Dim dsResults As DataSet dsResults = qry.ExecuteQuery("stringa di connessione", "SELECT * FROM utenti", Nothing) Console.Write(dsResults.GetXml) Console.ReadLine() End Sub End Module Come si può vedere abbiamo semplicemente creato un'istanza della
classe Querytor (che è in realtà stata generata tramite wsdl)
e su di essa richiamiamo il metodo ExecuteQuery come se fosse una
funzione qualunque. Si noti anche che è stato impostata la proprietà
Url di Querytor, che lato server non è stata definita, si
tratta infatti di un campo generato sempre da wsdl (o comunque
Visual Studio), che permette di impostare a run-time il percorso in cui
si trova il WebService, cosicché con la stessa classe sia possibile
interagire con più WebService identici ma su server differenti. In
questo caso l'URL corrisponde a quello originale (ovvero quello
specificato come parametro a wsdl o nella finestra di Visual
Studio), dunque era del tutto superfluo specificarlo. Cosa succede effettivamente quando si chiama un WebService. La chiamata a ExecuteQuery fa sì che il framework effettui una richiesta HTTP POST verso l'URL specificato simile a questa: POST /webservice.asmx HTTP/1.1 User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.1433) VsDebuggerCausalityData: uIDPo4ZGNMgGl1VKlaRnOSdNPhcAAAAA4979HqMnrU2rrHkYxak3vogGy8D56FVDlRoZZLaeB7sACAAA Content-Type: text/xml; charset=utf-8 SOAPAction: "http://tempuri.org/ExecuteQuery" Host: localhost:81 Content-Length: 401 Expect: 100-continue Connection: Keep-Alive <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <ExecuteQuery xmlns="http://tempuri.org/"> <ConnectionString>stringa di connessione</ConnectionString> <Command>SELECT * FROM utenti</Command> </ExecuteQuery> </soap:Body> </soap:Envelope> È una semplice richiesta HTTP che invia del codice XML in formato
SOAP e nel quale, senza dilungarci, possiamo riconoscere un elemento ExecuteQuery
che contiene altri due elementi che racchiudono rispettivamente i valori
dei parametri ConnectionString e Command serializzati (il parametro
Parameters non è
presente in quanto è stato passato come Nothing). HTTP/1.1 200 OK Server: Microsoft-IIS/5.1 Date: Sun, 30 Nov 2008 16:21:33 GMT X-Powered-By: ASP.NET X-AspNet-Version: 2.0.50727 Cache-Control: private, max-age=0 Content-Type: text/xml; charset=utf-8 Content-Length: 1898 <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <ExecuteQueryResponse xmlns="http://tempuri.org/"> <ExecuteQueryResult> <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true"> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="Table1"> <xs:complexType> <xs:sequence> <xs:element name="Column1" type="xs:string" minOccurs="0" /> <xs:element name="Column2" type="xs:string" minOccurs="0" /> <xs:element name="Column3" type="xs:string" minOccurs="0" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> </xs:element> </xs:schema> <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"> <NewDataSet xmlns=""> <Table1 diffgr:id="Table11" msdata:rowOrder="0" diffgr:hasChanges="inserted"> <Column1>sdf</Column1> <Column2>134</Column2> <Column3>dd</Column3> </Table1> <Table1 diffgr:id="Table12" msdata:rowOrder="1" diffgr:hasChanges="inserted"> <Column1>sdasdf</Column1> <Column2>1234</Column2> <Column3>ddddd</Column3> </Table1> </NewDataSet> </diffgr:diffgram> </ExecuteQueryResult> </ExecuteQueryResponse> </soap:Body> </soap:Envelope> In questo caso invece possiamo individuare un elemento ExecuteQueryResult contenete i dati restituiti dalla funzione serializzati (un DataSet) suddiviso in una parte contenente la descrizione della struttura dei dati (elemento xs:schema e figli) e una parte con i dati veri e propri (elemento NewDataSet e figli). Rimandiamo una trattazione più dettagliata di questo responso ad un articolo a venire.
|
|||
<< INDIETRO | by VeNoM00 |