Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Web services, WCF e ASP.NET

Comunicazione tra sistemi distribuiti e grazie al WCF di Microsoft.
Comunicazione tra sistemi distribuiti e grazie al WCF di Microsoft.
Link copiato negli appunti

Windows Communication Foundation (da ora in poi WCF) è una delle quattro componenti della versione 3.0 del .NET Framework, che ha il principale scopo di rappresentare i modelli di comunicazione tra sistemi distribuiti.

Data la grande importanza delle architetture distribuite basate sui servizi (SOA), era necessario unificare in un unico componente i metodi fin'ora supportati dalle tecnologie Microsoft per il trasporto di informazioni in ambienti distribuiti. Prima dell'avvento di WCF infatti, ogni sviluppatore doveva utilizzare uno di questi meccanismi per implementare le proprie architetture:

  • Web Services
  • Web Services Enhancements (WSE)
  • Remoting
  • Enterprice Services
  • MSMQ (Message Queuing)

La scelta di uno tra questi era legata alla tipologia di applicazione da dover sviluppare e alle strutture su cui essa si doveva basare.

WCF - Windows Comunication Foundation mette assieme tutte queste tecnologie fornendo un modello unificato di programmazione (e relativa API) per la realizzazione di applicazioni interoperabili, il tutto basato su standard condivisi il che permette lo scambio di informazioni anche tra piattaforme tra loro non eterogenee.

In particolare WCF è stato strutturato in modo tale da offrire supporto per tutti gli standard WS-*, cioè per tutte quelle nuove specifiche che sono nate dopo l'avvento dei Web Service, che hanno come fine comune quello di favorire l'interoperabilità tra piattaforme diverse tra loro. Infine, come abbiamo già detto, questo nuovo modello unificato di programmazione è stato basato sulle linee guida delle architetture SOA.

Una delle novità interessanti di WCF sta nel fatto che possiamo creare un semplice servizio ed esporlo tramite IIS come Web Service (utilizzando quindi il protocollo HTTP) oppure, modificando le informazioni di configurazione del servizio stesso, renderlo utilizzabile attraverso il protocollo TCP/IP.

Struttura di un servizio WCF

Ogni servizio WCF ha la medesima struttura basata sui concetti di "service class" e "host application".

Figura 1. Servizi e applicazioni
Servizi e applicazioni

Un servizio è infatti rappresentato da una classe detta appunto classe di servizio, compilata in un'assembly .NET, che per essere eseguita va "ospitata" in applicazioni che fungono da "host" per il servizio stesso. Tali applicazioni possono essere IIS, WAS, servizi Windows o semplici eseguibili (quindi Windows Form, applicazioni WPF o console application).

Per garantire il successo di una comunicazione in un ambiente distribuito, l'applicazione host e i vari client (rispettivamente chi offre e chi consuma il servizio) parlano tra loro attraverso delle "terminazioni" dette Endpoint.

WCF - Endpoint

Possiamo pensare all'Endpoint come ad un paradigma logico per spedire e ricevere i messaggi Ogni Endpoint è formato da tre componenti logiche, che rispondono ad altrettante domande (dove? come? cosa?):

  • Address - parametro che identifica la posizione (location) del servizio: un URI per la comunicazione tra client e servizio condivisa da entrambi, che risponde alla domanda "dove ?". All'interno di questo indirizzo viene specificato anche il protocollo di comunicazione (HTTP, TCP, etc.).
  • Binding - uno o più elementi per la configurazione del tipo di protocollo di comunicazione utilizzato e per la specifica di impostazioni aggiuntive e specifiche per il tipo di protocollo scelto. Per ogni tipo di binding è necessario specificare una ben precisa configurazione. Tali oggetti rispondono alla domanda "come ?".
  • Contract - rappresenta l'interfaccia esposta dal servizio e condivisa con i vari client, che specifica l'elenco delle operazioni fornite dal servizio stesso. Il contratto risponde alla domanda "cosa?".

Si tratta dell'ABC di WCF, che ci permetted di definire le operazioni e i modi con cui esse vengono eseguite.

Come è logico, ogni client si interfaccia con un solo Endpoint mentre il server ne gestisce diversi con protocolli e modalità differenti.

Figura 2. Struttura di una comunicazione WCF
Struttura di una comunicazione WCF

Le caratteristiche di ogni singolo endpoint (Address, Binding e Contract), devono essere specificate sia nei file di configurazione dell'applicazione ospite (IIS ad esempio), sia nei file di configurazione dei client. Queste informazioni saranno inserite nella sezione <system.serviceModel />.

Il Contract viene definito nelle service class, in cui si dichiarano:

  • funzionalità fruite (metodi decorati dall'attributo [OperationContract])
  • la struttura dei dati trasportati (classi o strutture decorate con l'attributo [DataContract])
  • messaggi utilizzati durante le successive comunicazioni (classi decorate dall'attributo [MessageContract])

Nel file di configurazione quindi specifichiamo quale classe definisce il nostro contratto.

Per realizzare un client, dobbiamo creare una classe proxy che rappresenti la "service class" (lato client) esposta dal servizio WCF (lato server), per poi definire il suo Endpoint all'interno del file di configurazione (nel caso di un'applicazione ASP.NET quindi, all'interno del file web.config).

Il nostro primo servizio WCF

Come primo esempio, andiamo sul classico e creiamo una Web application che consumi un servizio WCF (con estensione .svc) con il compito di ritornare la data e l'ora corrente. L'applicazione host per il nostro servizio sarà o IIS oppure il Web server integrato di Visual Studio.

Notiamo che, una volta aggiunto al proprio progetto di sviluppo un nuovo item di tipo "WCF Service", l'IDE creerà due oggetti:

  • un file con estensione .svc (il servizio vero e proprio)
  • un'interfaccia (file con estensione .cs) che definisce i metodi che il servizio dovrà poi implementare. Tale interfaccia rappresenta il contratto con cui il servizio sarà poi in grado di parlare nelle comunicazioni con i vari client

Per prima cosa, dobbiamo definire il contratto che contraddistingue il servizio WCF in se, definendo tutti i metodi che possono essere consumati dai client. Ognuno di questi, come abbiamo precedentemente detto, deve essere decorato con l'attributo [OperationContract].

Inoltre, onde evitare che tale servizio venga riconosciuto con lo spazio dei nomi "tempuri.org" (che rappresenta il namespace di default di ogni servizio), abbiamo definito un nostro namespace personalizzato attraverso una delle opzioni dell'attributo [ServiceContract].

namespace HelloWCF
{
    [ServiceContract(Namespace="http://www.peppedotnet.it", Name="DataService")]    
    public interface IDataService
    {
        [OperationContract]
        string GetCurrentDate();

        [OperationContract]
        string GetCurrentDateAndTime();
    }
}

Una volta definita l'interfaccia di base del nostro servizio WCF, possiamo scrivere la classe di servizio vera e propria, che implementa l'interfaccia appena vista.

namespace HelloWCF
{    
    public class DataService : IDataService
    {
        public string GetCurrentDate()
        {
            return DateTime.Now.ToShortDateString();
        }

        public string GetCurrentDateAndTime()
        {
            return DateTime.Now.ToString();
        }
    }
}

In altre parole, la service class è la classe di "code-behind" del file .svc, creato precedentemente.

<%@ ServiceHost Language="C#" Debug="true" Service="HelloWCF.DataService" CodeBehind="DataService.svc.cs" %>

Opzioni di configurazione WCF

Nella prima parte dell'articolo abbiamo visto come creare la "service class" e il contratto del nostro servizio WCF e come implementare le funzionalità da esporre al pubblico. Ora ci occupiamo di configurare l'applicazione host (IIS nel nostro caso) definendo i servizi, gli endpoint con i quali interfacciarsi, quindi l'indirizzo e tipologia di binding.

Queste informazioni vengono caricate in automatico dal processo host per garantire il corretto funzionamento dei servizi WCF esposti.

Per configurare i vari servizi, usiamo l'elemento <services>, figlio dell'elemento system.serviceModel, che è in grado di contenere un elenco di elementi di tipo "service". Nel nostro esempio abbiamo definito un solo servizio WCF da alloggiare nel processo host, per questo dobbiamo dichiarare un singolo elemento "service".

<services>
  <service behaviorConfiguration="DataServiceBehavior"
        name="HelloWCF.DataService">
  
    <endpoint address="http://localhost:49691/DataService.svc"
              binding="wsHttpBinding"
              contract="HelloWCF.IDataService">
    
      <identity><dns value="localhost" /></identity>
    </endpoint>
	
    <endpoint address="mex" 
              binding="mexHttpBinding"
              contract="IMetadataExchange" />
  </service>      
</services>

Nell'elemento <service> troviamo due attributi fondamentali per il funzionamento del servizio:

  • behaviorConfiguration, definisce il nome dell'elemento di configurazione che rappresenta il comportamento del servizio WCF. Vedremo in seguito tutte le specifiche di comportamento per servizi
  • name definisce univocamente il servizio nei confronti dei vari client

All'interno di definiamo gli Endpoint propri del servizio e, per ogni Endpoint, specifichiamo indirizzo, tipo di contratto e tipo di binding.

Nel nostro caso abbiamo due Endpoint, il primo per raggiungere il servizio tramite il protocollo HTTP (e quindi attraverso IIS), nel quale è stato specificato un indirizzo, l'interfaccia di contratto (precedentemente definita) e il binding di tipo wsHttpBinding. Il secondo Endpoint è stato aggiunto per definire l'indirizzo relativo al servizio di base.

Nota: in questo esempio si è deciso di esporre il servizio WCF precedentemente creato, solamente attraverso il tipo di binding "wsHttpBinding". L'estrema versatilità del framework di comunicazione Microsoft però, ci da anche la possibilità di esporre le medesime funzionalità di un singolo servizio in maniere differenti, sfruttando magari protocolli di comunicazione diversi. Attraverso le tecnologie che avevamo a disposizione prima dell'avvento del .NET Framework 3.0, un approccio del genere poteva essere raggiunto solamente sviluppando applicazioni differenti basate su tecnologie differenti.

Come ultimo step per la costruzione del nostro primo servizio WCF, dobbiamo specificare i comportamenti legati ad ogni singolo servizio offerto tramite l'applicazione host.

Specificando un comportamento da allegare ad un servizio WCF, abbiamo la possibilità di definire alcune proprietà legate sia alla comunicazione, sia allo sviluppo del servizio stesso. È possibile infatti definire credenziali specifiche per l'accesso al servizio, il tipo di autenticazione, se abilitare o meno la visualizzazione dei metadati del servizio o se abilitare o meno le funzionalità di debug del codice.

Tutto questo può essere specificato all'interno della sezione "behaviors/serviceBehaviors".

<behaviors>
  <serviceBehaviors>
    <behavior name="DataServiceBehavior">
      <serviceMetadata httpGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="false" />
    </behavior>
  </serviceBehaviors>
</behaviors>

Nel nostro caso, abbiamo esplicitato che i metadati del nostro servizio sono visibili anche su richieste di tipo GET e che, a fronte di malfunzionamenti, sarà possibile vedere i dettagli di ogni eccezione scatenata. Queste due opzioni di configurazione ci permettono di muoverci con tranquillità durante la fase di sviluppo del servizio WCF.

Come usare WCF

Una volta completata questa parte di configurazione, il servizio è così pronto all'uso. Per vederlo in azione, possiamo aprire il browser e digitare l'indirizzo che abbiamo messo all'interno dell'attributo "address" dell'elemento "endpoint" precedentemente definito.

Figura 3. Pagina principale del servizio
Pagina principale del servizio

Come consumare il servizio

Per semplicità, si è deciso che la stessa applicazione Web che offre il servizio, farà anche da applicazione client; nulla ci vieta comunque, di consumare il nostro servizio WCF da un client Windows o da altri tipi di applicazioni.

Per prima cosa, aggiungiamo il riferimento al servizio all'interno dell'applicazione. Per farlo, clicchiamo con il tasto destro sul nome della nostra applicazione Web e selezioniamo la voce "Add Service reference ..."; si aprirà così una maschera che permette l'inserimento dell'indirizzo con cui raggiungere il servizio desiderato.

Figura 4. Inserire il riferimento al Web service
Inserire il riferimento al Web service

Attraverso questa operazione di aggiunta, Visual Studio no fa altro che creare la classe proxy per accedere alle funzionalità esposte dal servizio WCF ed inserire all'interno del file .config della nostra applicazione le varie sezioni di configurazione.

La creazione della classe proxy può essere anche fatta a mano eseguendo l'utility "svcutil.exe", un'utility a riga di comando che prende come input l'indirizzo a cui risponde il servizio WCF e crea due oggetti ben distinti:

  1. un file di codice .cs che rappresenta la classe proxy per accedere al servizio.
  2. Un file .config contenente le configurazioni da inserire all'interno dell'applicazione client.

Si è preferito lasciare a Visual Studio il compito di generare il tutto: possiamo trovare i blocchi aggiunti dnel file di configurazione della nostra applicazione. In primis il blocco rappresentato dall'elemento "client", contenente il singolo endpoint con cui il client partecipa alla comunicazione con il servizio WCF.

<client>
  <endpoint address="http://localhost:49691/DataService.svc"
        binding="wsHttpBinding"
        bindingConfiguration="WSHttpBinding_DataService"
        contract="DataServiceReference.DataService"
        name="WSHttpBinding_DataService">
        
    <identity>
      <dns value="localhost" />
    </identity>
    
  </endpoint>
</client>

Nella specifica di questo singolo endpoint abbiamo due attributi in più: "bindingConfiguration" e "name"; il secondo identifica l'endpoint attraverso un nome univoco, mentre il primo contiene il riferimento alla sezione di configurazione del tipo di binding scelto per la comunicazione con il servizio WCF.

Anche in questo caso, l'elemento di configurazione "system.serviceModel" ci mette a disposizione una sezione ben precisa per svolgere questo lavoro di configurazione, sezione rappresentata dall'elemento <bindings>. Tale elemento conterrà una collezione di figli ognuno relativo ad un singolo binding e ognuno con differenti attributi di configurazione.

Nel nostro caso quindi abbiamo dovuto specificare le impostazioni per il binding wsHttpBinding, in quanto è il tipo di binding che si è scelto per il servizio WCF e per la comunicazione con i vari client.

Abbiamo potuto specificare i tempi di timeout legati all'apertura/chiusura della comunicazione e alla ricezione/spedizione dei messaggi, il tipo di encoding dei messaggi, la lunghezza massima dei messaggi, e tante altre opzioni. Inoltre, sempre all'interno di tale sezione, possiamo specificare le logiche di sicurezza che preferiamo adottare al nostro servizio WCF.

<bindings>
  <wsHttpBinding>
    <binding name="WSHttpBinding_DataService" closeTimeout="00:01:00"
             openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
             bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
             maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
             textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">

      <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
      <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
      
      <security mode="Message">
        <transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
        <message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default" establishSecurityContext="true" />
      </security>
      
    </binding>
  </wsHttpBinding>
</bindings>

Come abbiamo detto, il contenuto di questa sezione viene generato automaticamente dall'IDE di sviluppo (o tramite l'utility svcutil.exe); poi viene lasciata allo sviluppatore la possibilità di cambiare i valori di tale configurazione, secondo quelle che sono le esigenze principali della propria applicazione.

Una volta definite le configurazioni anche per la parte client, possiamo finalmente consumare il servizio WCF da una qualsiasi pagina ASP.NET: creiamo quindi una nuova istanza della classe proxy generata automaticamente da Visual Studio (classe che nell'esempio viene chiamata "DataServiceClient") e richiamiamo uno dei due metodi pubblici definiti dal servizio WCF.

protected void Page_Load(object sender, EventArgs e)
{
    DataServiceClient service = new DataServiceClient();
    lblTime.Text = service.GetCurrentDateAndTime();
}

Nel nostro semplice esempio, abbiamo definito due metodi con il tipo di ritorno "string"; nulla ci avrebbe vietato però, di definire delle classi custom per il passaggio di molteplici informazioni secondo una struttura ben precisa. L'unica differenza, rispetto all'utilizzo di tipi di dati primitivi, è data dal fatto che queste strutture personalizzate vanno segnate con particolari attributi che le pongono a far parte effettiva del contratto con cui il servizio WCF si presenta ai vari client.

Conclusioni su WCF

WCF offre tantissime altre funzionalità per lo sviluppo di applicazioni distribuite basate sui concetti base di ogni architettura SOA e che non deve essere visto come una semplice evoluzione dei Web service. Inoltre, con Visual Studio 2008, lo sviluppo di servizi WCF è notevolmente facilitato rispetto al passato.

Ti consigliamo anche