WCF RESTful service: esposizione del servizio

Previously on "Zen Programming"...

Nel precedente post abbiamo visto come realizzare un servizio RESTful basato su WCF; ma ci siamo fermato al più bello, cioè a come "hostare" il servizio web su IIS. Prima di tutto abbiamo bisogno di un file ".svc"...eh, ma sarebbe troppo facile...no?

Quindi ho colto l'occasione per illustrarvi un'altra meravigliosa caratteristica di WCF, detta "service activation": l'handler del servizio sarà generato utilizzando solo la configurazione dell'applicazione, senza un file reale che funga da endpoint per l'invocazione.

Per far questo abbiamo bisogno di un "service host factory", cioè una classe che eseguirà la generazione (la "produzione", essendo "factory") dell'istanza del servizio basato sull'interfaccia "ISimpleRestService". Il codice è autoesplicativo: basta derivare la classe base "ServiceHostFactoryBase" ed eseguire l'override del metodo "CreateServiceHost", che non farà altro che generare un'istanza del servizio attraverso la classe .NET "ServiceHost" e il type della classe del servizio.

"Ok...tutto bello. Ma ora?" Ora vediamo come mettere tutti i pezzi insieme nella nostra configurazione, gentilmente ospitata dal file "Web.config" che Visual Studio ha generato per voi. Passiamo in rassegna le parti salienti. La sezione "serviceActivations" contiene la definizione dell'endpoint che dovrete invocare per accedere alle funzionalità del servizio. In questo caso ho optato per chiedere ad IIS di generarmi automaticamente un endpoint sull'indirizzo "http://[server]/SimpleRestService.svc", semplicemente dando l'indirizzo relativo, e dichiarando tramite "full-type-name" la classe che abbiamo scritto prima come "ServiceHostFactory".

Sono poi stato definiti i behavior del servizio e dell'endpoint; è molto importante notare che i servizi RESTful sono unicamente utilizzabili con "webHttp". Il binding deve anch'esso essere definito come "webHttpBinding" (in questo caso opportunamente integrato di varie impostazioni opzionali di "readerQuotas"...ma ci torneremo un'altra volta).

In ultimo impostiamo le caratteristiche del servizio, definendo il nome del servizio stesso, l'endpoint di riferimento (a partire dall'indirizzo di "attivazione"), tutti i binding, i behavior, e il contratto "ISimpleRestService" che ne definisce i metodi.

Siamo in dirittura d'arrivo: apriamo il nostro browser preferito (e spero per voi che non sia Internet Explorer ;) ) e puntiamo al link "http://[server]/[virtual-directory]/SimpleRestService.svc". Vi sarà mostrata la "solita" pagina di cortesia di WCF. Modifichiamo ancora l'indirizzo e aggiungiamo "/ServerTime" per invocare il metodo che ritorna la data corrente del server. Il risultato? Una magnifica stringa in formato Json che ritorna il dato richiesto.

Non ne avete abbastanza? Chiamiamo "/Products/phones" per invocare il metodo che restituisce l'elenco dei prodotti della categoria "phones". Ma ricordiamoci prima una cosa: la chiamata dei prodotti è stata configurata sul verb "POST", quindi chiamandola da browser vi verrà restituito un errore di tipo "Method not allowed". Ma vogliamo comunque vedere se funziona, quindi andiamo alla definizione fatta nell'interfaccia "ISimpleRestService" e modifichiamo il verb di risposta a "GET", ricompiliamo il tutto, rilanciamo la chiamata sull'url giusto e...

...et voilà! Abbiamo finalmente finito. Ci sono o non ci sono voluti "5 minuti"? Come sempre, del resto...

Commenti

  1. Ciao Emanuele.
    Ho dato un'occhiata al tuo post e ho notato che quello che stai cercando di esporre è un servizio basato su motore WCF, ma di tipo SOAP: il mio articolo tratta invece la tipologia REST, meno "verbosa" di SOAP, più leggera ed agile. Chiaramente la scelta dipende molto dall'uso che devi farne, ma negli ultimi tempi REST è sicuramente quello più usato...

    Detto questo, mi pare che le configurazioni che hai impostato siano un po' "legate" a quello che è l'ambiente su cui saranno messe in esecuzione. Fai spesso uso di "identity" sul "dns" e di "baseAddress". Se stai cercando di pubblicare su una macchina di produzione, devi pensare che ci possono essere situazioni architetturali con altri server a monte di quello dove è installata la tua applicazione; quindi l'indirizzo base potrebbe non essere visibile alla macchina di esecuzione stessa, causando quindi fallimenti nel motore di WCF che non è in grado di creare i binding giusti.

    In secondo luogo (e dalla tua configurazione putroppo non riesco a capirlo) devi considerare che le varie versioni di WCF si comportano in maniera differente rispetto alle configurazioni imposte da Web.config. Per esempio, la prima versione presente in .NET Framework 3.0 richiedeva tutte le configurazioni esplicite; con le successive si è delegato molto alle configurazioni di default.

    Proprio in questa ultima considerazione ti consiglierei di fare una cosa. Prova a creare una nuova soluzione in Visual Studio, inserendo un progetto WCF hostato da IIS con le impostazioni di base. Quindi aggiungi un client (una banale applicazione console va benissimo) e verifica con queste impostazione di riuscire a contattare il metodo di sample che ti viene proposto.

    Se sulla tua macchina di sviluppo tutto funziona correttamente, pubblica l'intero contenuto sul dominio finale, e cambia i settings della tua applicazione console client in modo che l'endpoint target punti al dominio di destinazione (magari prima verifica che la tua "pagina di cortesia" sull'SVC funzioni correttamente).

    Ricordati infine che l'esposizione dipende molto anche dalla versione di IIS di cui gira il tutto: dai un'occhiata alla sezione "" e a "".

    Infine, se vuoi avere un dettaglio di errore esplicito, ricordati di applicare un behavior che abbia abilitate le istruzioni di debug esplicitamente (tag ...).

    Spero di esserti stato utile in qualche modo.
    Ciao
    M.

    RispondiElimina
  2. il behaviour mi sembra impostato correttamente, pubblico il servizio su un ip pubblico con IIS 7. Quando però in vstudio cerco di aggiornare / configurare il servicereference mi scontro con il fatto che ci si aspetta una cartella mex/_vti_bin/ListData.svc/$metadata che non esiste sul server, non viene pubblicata.. :-( poi questo è solo il primo passo

    ho provato anche a commentare l'identity localhost e aggiungere un baseAddress che punta al server di produzione, ma nulla

    RispondiElimina
  3. In realtà il messaggio di errore che ti viene generato non è referito alla mancanza di una "cartella" sul server, ma al fatto che il tuo endpoint deve essere configurato per pubblicare un contratto di binding che implementa l'interfaccia "IMetadataExchange". Mancando questo particolare settaggio nel tuo servizio, la funzione "Add Service Reference" di Visual Studio non riesce a fare il discovery della firma del servizio stesso (dei metadati che pubblica), e quindi della firma dei metodi.

    Ti ripeto, è sicuramente un problema di configurazione legata alla tua infrastruttura di rete. Il problema è che non avendo sottomano il codice e le configurazioni per esteso, non è facile fare una diagnosi. Metaforicamente, è come se cercassi capire quale problema ha il motore della mia macchina senza aprire il cofano... ;)

    Posso suggertiti un brevissimo post di Jeff Barnes che riporta un esempio molto molto semplice? http://jeffbarnes.net/blog/post/2006/10/16/metadata-exchange-endpoint.aspx. Magari fa al caso tuo, e riesci a risolvere la questione...

    Inoltre mi sentirei di consigliarti di non modificare le parti al "dns" e il "baseAddress": sono diversi anni che "traffico" con WCF, e veramente solo pochissime volte ho avuto la necessità di modificare quelle impostazioni per far funzionare tutto...

    Se il problema si verifica solo sul tuo IIS di Produzione, magari è solo un problema di registrazione di WCF in IIS. Prova a lanciare da un command "Run as Administrator" il comando "aspnet_regiis.exe -i" (lo trovi in "C:\Windows\Microsoft.NET\Framework[32 o 64 bit]\v[versione di .NET]\"...

    RispondiElimina

Posta un commento

Post popolari in questo blog

Cancellazione fisica vs cancellazione logica dei dati

Restore di un database SQL Server in un container Docker

Costruire una DataSession custom con Chakra.Core