W tym poście postaram się zaprezentować jak należy tworzyć serwis WCF oraz definiować metody jakie serwis udostępnia. Dodatkowo, bazując na "training kit" postaram się opisać najważniejsze aspekty, parametry i atrybuty, których programista serwisu WCF używa na co dzień.
Struktura WCF opiera się o trzy podstawowe kontrakty:
- Service contract, który definiuje jakie operacje serwis udostępnia aby mogły być wywoływane przez aplikacje klienckie. Kontrakt ten można określić jako "punkt startowy" przy tworzeniu naszego serwisu
- Data contract, który definiuje strukturę danych, która będzie przenoszona wraz z wiadomościami wysyłanymi z oraz do serwisu
- Message contract, który umożliwia kontrolowanie nagłówków (headers) oraz struktury wiadomości.
Tworzenie serwisu
Aby stworzyć nasz pierwszy serwis musimy posłużyć się poniższą konstrukcją:
[ServiceContract] public interface ITestService { [OperationContract] int AddSomeTestValue(string someTestValue); }
Możemy również stworzyć klasę jednak dobrą praktyką jest aby interfejs określał nasz serwis. Wtedy możemy w prosty sposób zaimplementować różne wersje serwisu jeśli jest taka potrzeba. Generalnie, tworzenie serwisu zaczynamy od stworzenia interfejsu, który następnie zostanie zaimplementowany w naszej docelowej klasie, która będzie zawierała implementację serwisu. Atrybut [ServiceContract] (zdefiniowany w System.ServiceModel), określa iż typ, który jest tym atrybutem udekorowany jest naszym serwisem. Atrybut ten może być zastosowany dla klasy lub interfejsu - co za tym idzie, tylko klasa lub interfejs mogą odgrywać rolę serwisu. Należy pamiętać, iż atrybut ten nie jest dziedziczony. Oznacza to, iż w przypadku gdy typ, którego chcemy użyć do stworzenia serwisu, dziedziczy z innego serwisu, to typ ten zawsze musi być udekorowany atrybutem [ServiceContract].
Atrybut [ServiceContract] można zdefiniować z odpowiednimi parametrami np:
[ServiceContract(Name="TestService", Namespace="http://mynamespace.com/2011/04/Test)]
[ServiceContract(Name="TestService", Namespace="http://mynamespace.com/2011/04/Test)]
Dozwolone parametry:
- Name - określa inną nazwę kontraktu (w przypadku gdy np. chcemy aby nasz kontrakt wygenerowany przez aplikację kliencką nazywał się TestService zamiast ITestService, w związku z tym zawsze starajmy się nadawać odpowiednią nazwę naszemu serwisowi).
- Namespace - określa docelową przestrzeń nazw w pliku WSDL. Dobrą praktyką jest podanie tego parametru.
- CallbackContract - przypisuje dodatkowy "Service contract" jako "Callback contract". Generalnie używany w przypadku gdy struktura naszego serwisu będzie opierała się na duplex MEP
- ProtectionLevel - określa jak wiadomości i metody naszego serwisu są zabezpieczone
- ConfigurationName - określa nazwę atrybutu serwisu, określonego w pliku konfiguracyjnym. Domyślnie atrybut ten jest taki sam jak klasa implementująca serwis.
- SessionMode - Określa, czy sesje są włączone w naszym serwisie
Atrybut [OperationContract] (zdefiniowany w System.ServiceModel), może być zastosowany jedynie dla metod. Atrybutu tego używa się aby zadeklarować, iż dana metoda należy do serwisu, dzięki czemu aplikacje klienckie, które będą korzystały z serwisu będą mogły taką metodę wywołać. Dla tego atrybutu również istnieje szereg parametrów:
- Name - określa inna nazwę metody
- Action - kontroluje akcją nagłówka (action header) dla wiadomości w tej metodzie
- ReplyAction - kontroluj akcję nagłówka (action header) dla wiadomości zwrotnej (response message) w tej metodzie
- IsOneWay - określa czy metoda jest jednokierunkowa (nie zapewnia odpowiedzi z serwisu)
- ProtecionLevel - określa jak metoda jest zabezpieczona
- IsInitiating - określa, czy wywołanie metody inicjuje nową sesję pomiędzy aplikacją wywołującą a serwisem
- IsTerminating - określa, czy wywołanie metody przerywa istniejącą sesję pomiędzy aplikacją wywołującą a serwisem
Parametrem godnym uwagi jest parametr Action. Otóż wspiera on używanie tzw: "Wildcards". Przykładem, kiedy możemy jej użyć jest sytuacja, w której metoda z tym parametrem ma być używana jako domyślna metoda - tzn. wywoływana zawsze gdy do serwisu zostanie wysłana prośba o wywołanie metody, o której nasz serwis "nie zna". Przykładem, może być zastosowanie parametru Action w sposób następujący:
[OperationContract(IsOneWay=true, Action="*")]
void ThisIsADefaultMethod(Message message);
Atrybut MessageParameter
Atrybut [MessageParameter] (zdefinowany w System.ServiceModel) kontroluje jak nazwy parametrów metody oraz zwracanych wartości wyświetlane są w opisie serwisu (WSDL). Atrybut ten posiada tylko jeden parametr: Name. Używa się go w następujący sposób:
[OperationContract]
[return: MessageParameter(Name="Output")]
public string SomeOperation([MessageParameter(Name="Input")] string myInputString);
zastosowanie powyższej konstrukcji sprawi, iż w opisie naszego serwisu będą widoczne parametry o nazwie "Output" i "Input" odpowiednio dla wiadomości zwrotnej metody SomeOperation jak i samej metody SomeOperation.
Fault contracts
WCF pozwala nam określić, jak nasz serwis będzie się zachowywał w przypadku wystąpienia błędu (fault). Oznacza to, iż możemy określić jakie wiadomości zostaną wysłane do klienta w przypadku wystąpienia błędu.
Należy zdawać sobie sprawę z tego iż owy błąd (fault) to nie to samo co wyjątek (exception). Wyjątki używane są, aby zakomunikować problem występujący podczas działania aplikacji. .NET framework pozwala na wyrzucanie, przechwytywanie, obsługiwanie lub ignorowanie wyjątków. Faults odnoszą się do błędów SOAP transmitowane z serwisu do klienta. Specyfikacja SOAP określa definicję takich błędów, określa jaką strukturę posiada taki błąd, który de facto "przenoszony" jest w wiadomości (message). Taka specyfikacja pozwala w ustandaryzowany sposób komunikować o błędach występujących w trakcie wywoływania serwisu.
WCF dostarcza klasę FaultException, która jest standardowym "mechanizmem tłumaczącym" pomiędzy wyjątkami .NET oraz błędami SOAP. Oznacza to, że jeśli serwis wyrzuci FaultExeption, wbudowany mechanizm WCF za pomocą serializacji zwróci do klienta odpowiedni błąd SOAP (SOAP fault). Klasa ta posiada dwie wersje: podstawową i generyczną: FaultException oraz FaultException
Określamy FaultContract dla odpowiedniej metody:
[ServiceContract]
public interface ITestService
{
[OperationContract]
[FaultContract(typeof(string))]
public int SomeOperation(int someIntParameter);
}
a następnie w implementacji naszego serwisu:public class TestServiceImplementation : ITestServiceImplementation
{
public int SomeOperation(int someIntParameter)
{
if(someIntParameter==0)
throw new FaultException("someIntParameter cannot be zero");
return someIntParameter*10;
}
}
Wszystko bardzo przystępnie opisane. Cieszę się, że tutaj trafiłem, bo też się przygotowuję do tego egzaminu :-)
OdpowiedzUsuńdziękuję za komentarz. Niestety egzamin już zdałem a dalszym postów nie zamieszczałem. Mam je opracowane w formie tekstowej. Jeśli więc będzie potrzeba i czas zamieszczę je tutaj.
OdpowiedzUsuń