Pokazywanie postów oznaczonych etykietą service. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą service. Pokaż wszystkie posty

poniedziałek, 28 października 2013

[WCF] Address, Binding

Zdefiniowanie serwisu WCF wymaga określenie tzw. ABC, czyli adresu, bindingów i kontraktu, które odpowiadają na pytania gdzie ? jak ? i co ?

Adres definiuje, gdzie serwis jest hostowany. WCF wspiera różne sposoby przesyłania danych, a co za tym idzie różne są rodzaje adresów. Poniższa tabela przedstawia sposoby adresowania w WCF.

Technologia Przykładowy adres Opis
HTTP http://localhost:8001 Najczęściej używany tekstowy sposób przesyłania danych, dane przeważnie w XML lub JSON
HTTPS https://localhost:8001 Bezpieczny sposób przesyłania danych po HTTP. Szyfrowanie za pomocą TLS lub SSL.
TCP Peer network net.tcp://localhost:8002/Service1, net.p2p://localhost/ Bardzo szybki sposób przesyłania danych w formacie binarnym.
IPC (Inter-process communication over named pipes) net.pipe://localhost/PipeService1 Szybki sposób przesyłania danych w obrębie jednej maszyny (współdzielona pamięć).
MSMQ (Microsoft Message Queue) net.msmq://localhost Wiadomości wysyłane przez klienta są kolejkowane. Użyteczny sposób dla scenariuszy, w których istnieje możliwość rozłączenia klienta z serwerem.

Kluczowym elementem architektury WCF jest binding, odpowiadający na pytanie "jak?". Główne obszary, jakie definiuje binding to protokół transportu danych, format wiadomości oraz szczegóły przesyłania wiadomości specyficzne dla danego protokołu. WCF udostępnia szereg protokołów, niemniej jednak zawsze istnieje możliwość zdefiniowania własnego.Poniżej przedstawiono listę bindingów dostarczanych przez WCF. Te, które rozpoczynają się od przedrostka "net" są dedykowane dla technologii .NET, z kolei bindingi rozpoczynające się od "ws" reprezentują ogólnie przyjęte standardy implementowane w wielu technologiach.

Binding Opis
basicHttpBinding Wiadomości przesyłane po Http i zakodowane w Utf-8. Oparte na WS-I profile.
webHttpBinding Serwisy wystawiane jako requesty Http, format XML lub JSON umożliwia tworzenie serwisów w oparciu o wzorzec REST.
wsHttpBinding Korzysta z zaawansowanych profili WS-*, takich jak WS-Security, WS-Transactions, WS-BusinessActivity, etc.
wsDualHttpBinding Korzysta z tych samych profili, co wsHttpBinding ale wspiera komunikację typu duplex.
wsFederationHttpBinding Profile WS-* rozszerzone o federated identity.
netTcpBinding Wiadomości binarne przesyłane po TCP. Wymaga, aby obie strony korzystały z WCF.
netNamedPipeBinding Zoptymalizowany pod kątem komunikacji w obrębie jednej maszyny.
netPeerTcpBinding Komunikacja po TCP z wykorzystaniem technologii P2P.
netMsmqBinding Asynchroniczna komunikacja z wykorzystaniem kolejki komunikatów.

Bindingi można ustawiać w sposób proceduralny...

ServiceHost host = new ServiceHost(typeof(EvaluationService));

host.AddServiceEndpoint(typeof (IEvaluationService), 
    new BasicHttpBinding(), "http://localhost:8080/evals/");

...ale znacznie wygodniej jest robić to w ustawieniach projektu (App.config lub Web.config), gdzie mamy do dyspozycji narzędzie wbudowane w Visual Studio.


W oknie tym możemy ustawić behavior dla określonego typu bindingu zmieniając domyślne parametry. Po zamknięciu okna zmiany zostaną przeniesione do pliku XML z konfiguracją. Na przykład:

<configuration>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="MyBindingConfiguration" allowCookies="true" />
      </wsHttpBinding>
    </bindings>
    <services>
      <service name="EvaluationServiceLibrary.EvaluationService">
        <endpoint address="/evals/" binding="wsHttpBinding" bindingConfiguration="MyBindingConfiguration"
          contract="EvaluationServiceLibrary.IEvaluationService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8732/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

niedziela, 27 października 2013

[WCF] ServiceContract, DataContract, MessageContract

Kontrakty w WCF można porównać do kontraktów w życiu codziennym. Podpisując jakiś kontrakt obie strony potwierdzają, że są świadome pewnych warunków. W WCF kontrakt definiuje między innymi wystawione serwisy, adresy, rodzaje wiadomości, jakie te serwisy odbierają i zwracają. WCF wykorzystuje  WSDL (Web Services Description Language) oraz XSD do dostarczenie metadanych na temat wystawionych serwisów. Ważne jest, aby kontrakty były niezależne od technologii, dlatego definiuje się je w neutralnym formacie bazującym na XML. Dzięki temu WebSerwisy wystawione na przykład w technologiach Javy mogą być wykorzystywane przez proxy WCF i aplikację napisaną w .NET i na odwrót.

ServiceContract

Definiuje funkcjonalność wystawioną przez serwis WCF. Dobrą praktyką jest oznaczanie tym atrybutem interfejsu .NET tak, aby zapewnić pewną warstwę abstrakcji nad konkretnymi implementacjami. Przykładowo:

[ServiceContract]
public interface IEvaluationService
{
    [OperationContract(IsOneWay = true)]
    void SubmitEvaluation(Evaluation eval);
    [OperationContract]
    List<Evaluation> GetEvaluations();
}

Property IsOneWay oznacza, że dana operacja nie zwraca wyniku. Konkretną implementację interfejsu można konfigurować poprzez atrybut ServiceBehavior.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
    ConcurrencyMode = ConcurrencyMode.Single)]
public class EvaluationService : IEvaluationService
{
    private List<Evaluation> _evals = new List<Evaluation>();

    public void SubmitEvaluation(Evaluation eval)
    {
        _evals.Add(eval);
    }

    public List<Evaluation> GetEvaluations()
    {
        return _evals;
    }
}

InstanceContextMode:  Single - Singleton, PerCall - zawsze nowa instancje, Session - każdy klient dostaje unikalną instancje w obrębie sesji.
ConcurrencyMode - Single - tylko jeden wątek ma dostęp do obiektu, Multiple - dostępny dla wielu wątków, Reentrant - nowe operacje mogą być wykonywane tylko podczas wywoływania innego serwisu.

DataContract

Definiuje typy, które mają być serializowane i przesyłane dalej przez WCF. Domyślny serializator to DataContractSerializer, który sprawdza, które obiekty są oznaczone tym atrybutem. Serializowane będą te propercje lub pola, które są oznaczone atrybutami DataMember.

[DataContract]
public class Evaluation
{
    [DataMember(IsRequired = true)]
    public string Submitted { get; set; }
    [DataMember(Order = 2)]
    public DateTime TimeSent { get; set; }
    [DataMember]
    public string Comments { get; set; }
}

Jeżeli w odebranej wiadomości nie będzie wartości, którą możnaby zdeserializować na property oznaczone z opcją IsRequired, aplikacja zwróci błąd. Poprzez opcję Order możemy ustawiać kolejność elementów w XML-u.

Problem może pojawić się w sytuacji, gdy chcemy wykorzystać mechanizm polimorfizmu. Jeżeli w sygnaturach metod mamy klasy bazowe, natomiast chcemy zwrócić klasę po nich dziedziczącą musimy użyć atrybutu KnownType. Znane typy dziedziczące po klasie bazowej zostaną dodane do kontraktu..

[DataContract]
[KnownType(typeof(Car))]
public abstract class Vehicle
{
    [DataMember]
    public string Name { get; set; }
}

[DataContract]
public class Car : Vehicle
{
    [DataMember]
    public int Year { get; set; }
}

MessageContract

Pozwala na zdefiniowanie, które pola mają znajdować się w nagłówku SOAP, a które w body. Typ oznaczony typ atrybutem przekazujemy do metody oznaczonej atrybutem OperationContract. 

[DataContract]
public class Evaluation
{
    [DataMember(IsRequired = true)]
    public string Submitted { get; set; }
    [DataMember(Order = 2)]
    public DateTime TimeSent { get; set; }
    [DataMember]
    public string Comments { get; set; }
}

[DataContract]
public class CustomHeader
{
   [DataMember]
   public string Username { get; set; }
}

[MessageContract]
public class EvaluationRequest
{
    [MessageHeader]
    public CustomHeader EvaluationHeader { get; set; }

    [MessageBodyMember]
    public Evaluation EvaluationBody { get; set; }
}

Dzięki temu wiadomość SOAP przyjmuje poniższą postać:

<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://tempuri.org/IEvaluationService/SubmitEvaluation</a:Action>
    <h:EvaluationHeader xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://tempuri.org/">
      <Username xmlns="http://schemas.datacontract.org/2004/07/EvaluationServiceLibrary">Joe</Username>
    </h:EvaluationHeader>
  </s:Header>
  <s:Body>
    <EvaluationRequest xmlns="http://tempuri.org/">
      <EvaluationBody xmlns:d4p1="http://schemas.datacontract.org/2004/07/EvaluationServiceLibrary" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <d4p1:Comments>Git</d4p1:Comments>
        <d4p1:Submitted>Yes</d4p1:Submitted>
        <d4p1:TimeSent>2013-10-27T12:46:00</d4p1:TimeSent>
      </EvaluationBody>
    </EvaluationRequest>
  </s:Body>
</s:Envelope>

wtorek, 8 października 2013

[WCF] Wprowadzenie

Windows Communication Foundation to technologia, którą świat ujrzał z .NET Framework 3.0 jako odpowiedź na potrzebę budowania rozproszonych systemów, które MS zaczął marketingowo nazywać "Connected Systems". Podczas tworzenia aplikacji rozproszonych ważne jest, aby umożliwić komunikowanie się systemów napisanych w różnych technologiach. Stąd pomysł serwisów jako jednostek udostępniających pewne funkcjonalności poprzez przesyłanie ustandaryzowanych wiadomości.

Dominują dwa wzorce, SOAP i REST.

SOAP (Simple Object Access Protocol) - wiadomości wysyłane są w formacie XML i WS-* protocols. Wspiera wiele protokołów transportowych np. HTTP, TCP, MSMQ.

REST (Representational State Transfer) - różne formaty wiadomości (XML, coraz częściej JSON), protokół transportowy HTTP, paradygmat ukierunkowany na adresowanie, modyfikowanie i odczytywanie zasobów.

WCF wprowadza unifikację, umożliwiając wymianę wiadomości po różnych protokołach, w różnych formatach, wzorcach.

Większość funkcjonalności WCF zawarte jest w System.ServiceModel.dll. Najprościej filozofię WCF oddaje zdanie: serwisy wystawiają swoje funkcjonalności poprzez endpointy, natomiast klienci konsumują je poprzez kanały. WCF-owe endpointy składają się z trzech składowych ABC definiujące podstawowe pojęcia (Address - gdzie?, Binding - jak?, Contract - co?).

Tworzenie najprostszej aplikacji WCF:

Pierwszy projekt to serwisy WCF.

[DataContract]
public class Name
{
    [DataMember]
    public string First { get; set; }

    [DataMember]
    public string Last { get; set; }
}

[ServiceContract]
public interface IMyFirstService
{
    [OperationContract]
    string SayHello(Name name);
}

public class MyFirstService : IMyFirstService
{
    public string SayHello(Name name)
    {
        return string.Format("Hello, {0} {1}", name.First, name.Last);
    }
}

Klasa Name będzie przesyłaną wiadomością, natomiast oznaczenie interfejsu przez atrybuty definiuje, jakie operacje chcemy wystawić na zewnątrz. Kolejne zmiany nanosimy w pliku App.config.Wystawiamy jeden serwis dostępny pod trzema endpointami wykorzystującymi różne protokoły transportowe.

<services>
  <service name="WcfEssentials.MyFirstService">
    <host>
      <baseAddresses>
        <add baseAddress="http://localhost:8080/helloworld/"   />
      </baseAddresses>
    </host>
    <endpoint address="ws"  binding="wsHttpBinding" contract="WcfEssentials.IMyFirstService" />
    <endpoint address="basic"  binding="basicHttpBinding" contract="WcfEssentials.IMyFirstService" />
    <endpoint address="net.tcp://localhost:8081/helloworld/"  binding="netTcpBinding" contract="WcfEssentials.IMyFirstService" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
  </service>
</services>

Uruchomienie aplikacji spowoduje pojawienie się okna, w którym możemy testować nasze endpointy.


Jeżeli chcemy wykorzystać nasz serwis w kodzie, dodajemy osobny projekt, np. aplikację konsolową. Następnie musimy dodać referencję do serwisów WCF. Klikamy prawym przyciskiem myszy i wybieramy Add Service Reference....


Strona kliencka pobierze metadane i utworzy obiekty proxy, którymi będziemy mogli się łączyć do stworzonych wcześniej serwisów. W pliku app.config automatycznie wygenerują się bindingi dla strony klienckiej, gdzie każdy endpoint otrzyma swoją nazwę. Wywołanie serwisu w kodzie C# jest bardzo proste.

using (var client = new MyFirstServiceClient("NetTcpBinding_IMyFirstService"))
{
    var name = new Name() { First = "Tom", Last = "Smith" };
    Console.WriteLine(client.SayHello(name));
    Console.Read();
}

Jako parametr konstruktora podajemy wygenerowaną przez VS nazwę serwisu.

piątek, 23 sierpnia 2013

[C#|Visual Studio] ServiceStack: Tworzenie WebSerwisów

W tym poście zostanie opisane, jak utworzyć prosty projekt z RESTowymi serwisami w oparciu o ServiceStacka. Zaczynamy od stworzenia w Visual Studio pustego projektu ASP .NET.


Następnie za pomocą NuGeta instalujemy dllki ServiceStack.


Następnie tworzymy proste obiekty DTO, do których serializowane i deserializowane będą responsy i requesty REST-owe. Dobrą praktyką jest, aby obiekty DTO były symetryczne i przez konwencję do nazwy obiektu zwrotnego dodajemy "Response".

[Route("/entry/{Date}", "GET")]
public class Entry
{
    public DateTime Date { get; set; }
    public int Count { get; set; }
}

public class EntryResponse
{
    public int Total { get; set; }
}

Poprzez atrybuty definiujemy routingi, w których fragmenty ścieżek można bindować bezpośrednio do properties DTO. Następnie dodajemy klasę serwisu, która dziedziczy po bazowej klasie Service z SeriveStack. W klasie dodajemy metody o takich nazwach jak czasowniki HTTP, gdzie parametrem będzie zdefiniowane wcześniej DTO.

public class EntryService : Service
{
    public int Sum { get; set; }

    public object Post(Entry entry)
    {
        Sum += entry.Count;
        return new EntryResponse() {Total = Sum};
    }
}

Aby wystartować serwis, wystarczy skorzystać z pliku .asax.

public class Global : System.Web.HttpApplication
{

    public class MyEntryAppHost : AppHostBase
    {
        public MyEntryAppHost()
            : base("MyEntryService", typeof(EntryService).Assembly)
        {
        }

        public override void Configure(Funq.Container container)
        {
            
        }
    }

    protected void Application_Start(object sender, EventArgs e)
    {
        new MyEntryAppHost().Init();
    }
    //...
}

Metoda Configure posłuży jako bootstrapper, gdzie będzie można konfigurować np. kontener IoC. Ostatnim etapem jest skonfigurowanie pliku Web.config, gdzie dodajemy odpowiedni handler.

<configuration>
    <system.web>
        <compilation debug="true" targetFramework="4.0" />
    </system.web>

  <system.web>
    <httpHandlers>
      <add path="*" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*"/>
    </httpHandlers>
  </system.web>

</configuration>

Po uruchomieniu aplikacji mamy możliwość podglądu i testowania naszych endpointów.

niedziela, 7 października 2012

[HTML|JS|CSS] AngularJS: Services

Dzięki zastosowaniu serwisów AngularJS wspiera wzorzec projektowy Dependency Injection. Wzorzec ten znany z wielu obiektowych języków programowania ułatwia testowanie komponentów oraz podejmowanie decyzji o zależnościach w czasie pracy aplikacji. W przypadku Angulara podczas uruchomienia strony tworzony jest obiekt zwany injectorem, zarządzający wszystkimi serwisami. W momencie, gdy chcemy wykorzystać któryś z serwisów, injector zwraca jego instancję lub inicjuje go, jeśli nie nastąpiło to wcześniej. Ma tu więc miejsce połączenie wzorców singletonu oraz lazy-load. Samo wstrzykiwanie zależności można uzyskać na dwa sposoby: niejawnie podając nazwę serwisu jako argument funkcji (np. kontrolera), lub mechanizmem wstrzykiwania. Sposób pierwszy, choć czytelniejszy, napotyka jedną poważną przeszkodę. Podczas minifikacji i obfuskacji pliku ze skryptem zostają zmieniane nazwy zmiennych, również argumentów funkcji, przez co będziemy mieli do czynienia z zupełnie inną logiką. Drugi zapis, nieco dłuższy naprawia ten problem, kosztem czytelności kodu.
//DEPENDENCY INJECTION

function firstController($scope, $window){
 $scope.text = "firstController";
 $scope.introduce = function(e){
  $window.alert($scope.text);
 }
}

function secondController(s, w){
 s.text = "secondController";
 s.introduce = function(e){
  w.alert(s.text);
 }
}
secondController.$inject = ['$scope','$window'];

Mamy także możliwość tworzenia własnych serwisów dla naszych modułów. Dostarczając definicję, dostajemy w prezencie opisaną powyżej funkcjonalność, łącznie z możliwością wstrzykiwania naszego serwisu do kontrolera. Przykład poniżej.
angular.module('services', [], function($provide) {
    $provide.factory('mylogger', function() {
     var counter = 0;
     return function(text){
      counter += 1;
      console.log("Logging text number " + counter);
      console.log(text);
     }
    });
});

Przykład użycia w kontrolerze:
function firstController($scope, $window){
 $scope.text = "firstController";
 $scope.introduce = function(e){
  $window.alert($scope.text);
 }
}

Przez konwencję zaleca się, by nie nadawać własnym serwisom nazw rozpoczynających się od $, jako znaku zarezerwowanego dla serwisów Angulara.

sobota, 28 lipca 2012

[Wzorce projektowe] Service Locator

Analogia z życia:

Dzisiejsze telefony komórkowe oferuję wiele funkcjonalności. Możemy na przykład napisać wiadomość tekstową lub do kogoś zadzwonić. Z punktu widzenia użytkownika, wysłanie takiej wiadomości jest wykorzystaniem pewnej usługi. Piszemy wiadomość, podajemy numer i klikamy wyślij. To w jaki sposób wiadomość zostanie przekazana i ile za nią zapłacimy zależy między innymi od operatora sieci. Wkładając kartę SIM, możemy na starcie określić wspomniane warunki, aby później po uruchomieniu telefonu w każdej chwili korzystać z usługi w wybranej wersji.


Zastosowanie:

Wzorzec lokatora usług znajduje zastosowanie w sytuacji, gdy chcemy odseparować obiekty, od usług z których korzystają. Zwiększy to niezależność komponentów i ułatwi przyszłe wprowadzanie modyfikacji. Dodatkowo mając kilka podobnych serwisów, można wybierać któryś z nich przed uruchomieniem aplikacji przez odpowiednie ustawienia konfiguracyjne. Rozwiązanie takie wspiera testowalność.

Zasada działania:

Wprowadza się klasę lokatora usług, zawierającą statyczne pole typu HashTable z usługami. Kluczem do usługi w tablicy może być typ usługi lub podana nazwa. Dodatkowo klasa lokatora usług zawiera statyczne metody do pobrania i dodania nowego serwisu. Serwisy można dodawać na starcie aplikacji, np. w WPF czy Silverlight w metodzie OnStartup.

Przykład implementacyjny:

 public interface ILogger
    {
        void Write(string msg);
    }

    public class ConsoleLogger : ILogger
    {
        public void Write(string msg)
        {
            Console.WriteLine(msg);
        }
    }
internal class ServiceLocator
    {
        private static readonly Hashtable Services 
            = new Hashtable();

        public static void AddService<T>(T service)
        {
            Services.Add(typeof(T),service);
        }

        public static void AddService<T>(string name, T service)
        {
            Services.Add(name, service);
        }

        public static T GetService<T>()
        {
            return (T)Services[typeof(T)];
        }

        public static T GetService<T>(string name)
        {
            return (T)Services[name];
        }

    }
public class Fibonnaci
    {
        public int Number { get; set; }
        public int LastNum { get; set; }

        public Fibonnaci()
        {
            LastNum = 0;
            Number = 1;
        }

        public int Next()
        {
            var n = LastNum + Number;
            LastNum = Number;
            Number = n;
            IsDivisibleBy4();
            return n;
        }

        public void IsDivisibleBy4()
        {
            if (Number%4 != 0) return;
            var logger = ServiceLocator.GetService<ILogger>();
            logger.Write(String.Format
                             ("Number {0}, last : {1}",Number, LastNum));
        }
    }
class Program
    {
        static Program()
        {
            ServiceLocator.AddService<ILogger>(new ConsoleLogger());
        }

        static void Main(string[] args)
        {
            Fibonnaci fibonnaci = new Fibonnaci();
            for (int i = 0; i < 100; i++)
            {
                fibonnaci.Next();
            }
            Console.ReadKey();
        }
    }