© Martin Korneffel, Stuttgart 2004 +++ email: trac@n.zgs.de +++ web: www.s-line.de/homepages/trac

Webservice + WCF

Inhalt

  1. Was ist ein Webservice

    1. Vorteile von Webservices

    2. ASMX- Webservices

    3. WCF

  2. Einen Webservice nutzen

    1. Varianten zum Abruf eines ASMX- Webservices

  3. Technische Grundlagen

    1. Client/Server

    2. Web-, Service Referenzen und Proxyklassen

    3. HTTP

    4. SOAP

    5. WSDL

  4. WebServices erstellen

    1. ASMX WebService- Klassen

      1. Lebenszyklus eines ASMX Webservice- Objektes

    2. Implementieren einer ASMX- WebService- Klasse

      1. Attribute für WebService- Klassen

      2. Attribute für WebService- Methoden

      3. Behandlung von Ausnahmen

      4. Komplexe Parameter und Rückgabewerte

        1. Strukturen und Klassen

        2. Arrays und Listen

    3. Webservices mit "Gedächnis"

  5. Konsumieren von Webservices

    1. Suchen von Webdiensten

    2. Dienstveröffentlichungen mittels UDDI und DISCO

    3. Erstellen von Proxyklassen

    4. Asynchroner Aufruf von Webservices

      1. Rückgabewert IAsyncResult

  6. Schützen eines Webservices

    1. Variante 1: Login- Daten über SoapHeader senden

      1. Aufruf des gesicherten Dienstes über die Proxyklasse

    2. Variante 2 : Anmelden mit neuer Identität unter Windows

  7. Konfiguration des Webservices

    1. Debugging aktivieren

    2. Tracing aktivieren

    3. Definieren der Zugriffsmethoden

  8. WSE 3.0

    1. Installation von X509 Zertifikaten

    2. Zertifikatstool

    3. Konfigurationstool

    4. Konfiguration von Dienst und Client

    5. Integration von WSE

      1. Spezielle Basisklasse der Proxys auf der Clientseite

      2. Installation einer spezialisierten Classfactory für Soap- Protokolle auf der Serviceseite

    6. Effiziente Übertragung großer Datenblöcke mittels MTOM

    7. Windows Dienst als Webservice Host

    8. Sichern von Webdiensten durch Verschlüsseln von Soap Nachrichten

    9. Probleme mit WSE 3.0

  9. WCF-Service

    1. Endpunkte

    2. Bindungen

Was ist ein Webservice

Definition

Webservice

Ein Webservice ist ein Dienst im Netz, der standardisierten, herstellerunabhängige, formale Sprachen einsetzt um:

  • abrufbaren Methoden aufzulisten (z.B. WSDL)

  • die Datenstrukturen der Parameter und Rückgabewerte zu definieren (z.B. WSDL)

  • Methodenaufruf abzubilden (z.B. SOAP)

  • die Ergebnisse darzustellen (z.B. SOAP)



Vorteile von Webservices

ASMX- Webservices

Das ASP.NET Framework ermöglicht die Implementierung von Webservices auf IIS. Kern der Implementierung sind dabei Instanzen der Webservice- Klasse, die SOAP- und WSDL Requests über HTTP verarbeiten.

Mittels der WSE (Webservice Enhancements) Bibliothek wurden ASMX Webservices um Techniken zur effizienteren Übertragung von Binärdaten und Verschlüsselte Datenübertragung aufgerüstet.

WCF

Seit .NET 3.0 gibt es die Windows Communication Foundation Bibliothek. Mit ihr können Webservices unabhängig von ASP.NET und IIS implementiert werden. Zudem integriert sie Techniken aus der WSE. WCF verallgemeinert die Webservices zu einer Service- orientierten Technologie (SOA).

Einen Webservice nutzen


Varianten zum Abruf eines ASMX- Webservices

ASMX- Webservices sind mit allen Formen von HTTP- Requests abrufbar. So können die Aufrufparameter in einem Request als SOAP- Telegramme, Qeurystrings oder Formulardaten gesendet werden.


Achtung: Die Methoden Get und Post unterstützen keine komplexen Datentypen. Dafür ist ihr Einsatz resourcenschonender in kleinen Diensten gegenüber SOAP.

Technische Grundlagen

Im folgenden werden Grundbegriffe für das Verständnis von ASMX und WCF- Webdienste erläutert

Client/Server

Webdienste werden von Clients konsumiert. Ein Client kann ein z.B. ein Webbrowser, ein Kommandozeilenprogramm oder eine weitere Serveranwendung sein.

Web-, Service Referenzen und Proxyklassen

Die mit WSDL gelieferten Metadaten sind maschinell auswertbare XML Dateien, die den Webservice vollständig beschreiben. Entwicklungswerkzeuge wie Visual- Studio können aus diesen sog. Proxyklassen generieren, welche den Dienstaufruf über HTTP, und die Serialisierung/Deserialisierung der Parameter/Ergebnisse in Eigenschaften und Methoden kapseln.


Innerhalb eines Visual- Studio Projektes werden die Proxyklassen für ASMX- Webdienste unter Webservice- Referenzen, und für WCF- Dienste unter Service- Referenzen verwaltet.

HTTP

Definition

URL

Ein URL (engl.: Uniform Resource Locator) bezeichnet eine Resource im Internet und enthält explizit Anweisungen, wie auf diese Resource zugegriffen werden kann.

Allgemeiner Aufbau:

http://host[:port][path[?querystring]]

SOAP

Simple Object Access Protrocol

Allgemeiner Aufbau eines SOAP- Nachricht


Beispiel: Aufruf und Resultat von DMSsearch als SOAP- Nachrichten

POST /asp-mcsd2/DMSsearch/dmssearch.asmx HTTP/1.1
Host: localhost
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://www.tracs.de/search"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <search xmlns="http://www.tracs.de">
      <stichwort>string</stichwort>
    </search>
  </soap:Body>
</soap:Envelope>
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <searchResponse xmlns="http://www.tracs.de">
      <searchResult>
        <xsd:schema>schema</xsd:schema>xml</searchResult>
    </searchResponse>
  </soap:Body>
</soap:Envelope>

WSDL

Meta und Typinformationen über Webservices werden in einer standardisierten Sprache verfasst, genannt Webservice Description Language oder kurz WSDL. WSDL- Beschreibungen sind XML- Dateien, welches das XML- Schema- Vokabular einsetzt. ASP.NET erzeugt für einen Webservice diese Beschreibungen automatisch. Sie können über einen Uri mit dem Querystring WSDL zu jedem Dienst über das Web abgerufen werden:

http://localhost:1516/BlogWebSvr/BlogWebService.asmx?WSDL


Das Ergebnis ist z.B. eine WSDL- Datei wie folgende:

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://www.tracs.de/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="http://www.tracs.de/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
  <wsdl:types>
    <s:schema elementFormDefault="qualified" targetNamespace="http://www.tracs.de/">
      <s:element name="HelloWorld">
        <s:complexType />
      </s:element>
      <s:element name="HelloWorldResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="HelloWorldResult" type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="blog">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="Autor" type="s:string" />
            <s:element minOccurs="0" maxOccurs="1" name="Message" type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="blogResponse">
        <s:complexType />
      </s:element>
      <s:element name="GetAllBlogs">
        <s:complexType />
      </s:element>
      <s:element name="GetAllBlogsResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="GetAllBlogsResult" type="tns:ArrayOfBlogEntry" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:complexType name="ArrayOfBlogEntry">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="unbounded" name="BlogEntry" nillable="true" type="tns:BlogEntry" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="BlogEntry">
        <s:sequence>
          <s:element minOccurs="1" maxOccurs="1" name="Timestamp" type="s:dateTime" />
          <s:element minOccurs="1" maxOccurs="1" name="MsgId" type="s:int" />
          <s:element minOccurs="0" maxOccurs="1" name="Message" type="s:string" />
        </s:sequence>
      </s:complexType>
    </s:schema>
  </wsdl:types>
  <wsdl:message name="HelloWorldSoapIn">
    <wsdl:part name="parameters" element="tns:HelloWorld" />
  </wsdl:message>
  <wsdl:message name="HelloWorldSoapOut">
    <wsdl:part name="parameters" element="tns:HelloWorldResponse" />
  </wsdl:message>
  <wsdl:message name="blogSoapIn">
    <wsdl:part name="parameters" element="tns:blog" />
  </wsdl:message>
  <wsdl:message name="blogSoapOut">
    <wsdl:part name="parameters" element="tns:blogResponse" />
  </wsdl:message>
  <wsdl:message name="GetAllBlogsSoapIn">
    <wsdl:part name="parameters" element="tns:GetAllBlogs" />
  </wsdl:message>
  <wsdl:message name="GetAllBlogsSoapOut">
    <wsdl:part name="parameters" element="tns:GetAllBlogsResponse" />
  </wsdl:message>
  <wsdl:portType name="BlogWebServiceSoap">
    <wsdl:operation name="HelloWorld">
      <wsdl:input message="tns:HelloWorldSoapIn" />
      <wsdl:output message="tns:HelloWorldSoapOut" />
    </wsdl:operation>
    <wsdl:operation name="blog">
      <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Blog</wsdl:documentation>
      <wsdl:input message="tns:blogSoapIn" />
      <wsdl:output message="tns:blogSoapOut" />
    </wsdl:operation>
    <wsdl:operation name="GetAllBlogs">
      <wsdl:input message="tns:GetAllBlogsSoapIn" />
      <wsdl:output message="tns:GetAllBlogsSoapOut" />
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="BlogWebServiceSoap" type="tns:BlogWebServiceSoap">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="HelloWorld">
      <soap:operation soapAction="http://www.tracs.de/HelloWorld" style="document" />
      <wsdl:input>
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="blog">
      <soap:operation soapAction="http://www.tracs.de/blog" style="document" />
      <wsdl:input>
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="GetAllBlogs">
      <soap:operation soapAction="http://www.tracs.de/GetAllBlogs" style="document" />
      <wsdl:input>
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:binding name="BlogWebServiceSoap12" type="tns:BlogWebServiceSoap">
    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="HelloWorld">
      <soap12:operation soapAction="http://www.tracs.de/HelloWorld" style="document" />
      <wsdl:input>
        <soap12:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="blog">
      <soap12:operation soapAction="http://www.tracs.de/blog" style="document" />
      <wsdl:input>
        <soap12:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="GetAllBlogs">
      <soap12:operation soapAction="http://www.tracs.de/GetAllBlogs" style="document" />
      <wsdl:input>
        <soap12:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="BlogWebService">
    <wsdl:port name="BlogWebServiceSoap" binding="tns:BlogWebServiceSoap">
      <soap:address location="http://localhost:1516/BlogWebSvr/BlogWebService.asmx" />
    </wsdl:port>
    <wsdl:port name="BlogWebServiceSoap12" binding="tns:BlogWebServiceSoap12">
      <soap12:address location="http://localhost:1516/BlogWebSvr/BlogWebService.asmx" />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

WebServices erstellen

Wir schreiben das Jahr 2009, und es gibt mittlerweile mehrere Möglichkeiten unter .NET, Webservices zu implementieren.

In den Versionen bis einschließlich .NET 2.0 stützen sich Webservices voll auf ASP.NET und IIS. Ab 3.0 sind mit der WCF- Bibliothek auch Webservices außerhalb von ASP.NET und IIS implementierbar. So kann auch ein Kommandozeilenprogramm befähigt werden, Webservices anzubieten.

ASMX WebService- Klassen

Webdienste werden durch Klassen implementiert, die von der Klasse WebService abgeleitet sind:


Lebenszyklus eines ASMX Webservice- Objektes


Implementieren einer ASMX- WebService- Klasse

Für den praktischen Einsatz benötigt ASP.NET noch weitere Informationen zum Dienst, wie der Name, der zu verwendende Namespace und eine Kurzbeschreibung. Diese werden den Metadaten der Klasse durch das Attribut WebService hinzugefügt:

[WebService(Namespace="http://www.tracs.de",
                       Name="DMSSearch",
                       Description="Suche von Dokumenten bezüglich Stichworte")]
public class DMSSearch : System.Web.Services.WebService {

  [WebMethod] public DataSet search(string stichwort) 
  {
     DataSet ds = new DataSet();
      :
     return ds; 
  }
}

Attribute für WebService- Klassen


Beispiel:

[WebService(Namespace = "http://www.tracs.de/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class BlogWebService : System.Web.Services.WebService
{
   //...
}

Attribute für WebService- Methoden


Beispiel:

    const string IdInstanzCounter = "InstanzCounter";

    [WebMethod(Description = "Testet WebMethod Attribute",
               MessageName = "WebMethodenAttributTest",
               EnableSession = true)]
    public int TestWebMethod()
    {
        if (Session[IdInstanzCounter] == null)
            Session[IdInstanzCounter] = 0;
        else
            Session[IdInstanzCounter] = (int)Session[IdInstanzCounter] + 1;

        return (int)Session[IdInstanzCounter];
    }

Testumgebung für die Webmethode:

        // CookieContainer zum Aufbewahren der "Kekse" für Sessions
        System.Net.CookieContainer cookieContainer = new System.Net.CookieContainer();

        private void btnTestWebMethodAttribute_Click(object sender, EventArgs e)
        {
            localhost.TestWebSvr webSvr = new global::TestWebservices.localhost.TestWebSvr();

            // Zuweisen des CookieContainers zur Aufbewahrung der Cookies, über die 
            // Sitzungen identifiziert werden
            webSvr.CookieContainer = cookieContainer;
            log.LogMsg(0, "TestWebMethodAttribute: InstanzCount=" + webSvr.TestWebMethod().ToString());
        }

Behandlung von Ausnahmen

Beim werfen von Ausnahmen muß beachtet werden, daß diese serialisierbare Objekte darstellen. Im Framework wird hierfür die ideale Klasse System.Web.Services.Protocols.SoapException angeboten.


Beispiel:

Wegmehtode, die Ausnahmen auslöst

[WebService(Namespace = "http://www.tracs.de/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class TestWebSvr : System.Web.Services.WebService
{

    public TestWebSvr()
    {

        //Auskommentierung der folgenden Zeile bei Verwendung von Designkomponenten aufheben 
        //InitializeComponent(); 
    }  

    public enum ExceptionTypes
    {
        standard,
        soap,
        Null
    }

    [WebMethod(Description = "Testen von Ausnahmen")]
    public void TesteAusnahme(ExceptionTypes type)
    {
        switch(type) {
            case ExceptionTypes.standard:
                throw new Exception("Eine Standardausnahme");
                break;
            case ExceptionTypes.soap:
                throw new SoapException("Eine Soap Exception", SoapException.ClientFaultCode, Context.Request.Url.AbsoluteUri);
                break;
            default:
                throw new SoapException("Eine Serverfehler Soap Exeption", SoapException.ServerFaultCode, "ich");
        }
    }

}

Zugriff auf einen Webservice aus einer Applikation und Anfangen der Ausnahmen

        private void btnTesteAusnahmen_Click(object sender, EventArgs e)
        {
            localhost.TestWebSvr webSvr = new global::TestWebservices.localhost.TestWebSvr();

            try
            {
                webSvr.TesteAusnahme(global::TestWebservices.localhost.ExceptionTypes.standard);
            }
            catch (Exception ex)
            {
                log.LogMsg(0, "Standardausnahme: " + ex.Message);
            }

            try
            {
                webSvr.TesteAusnahme(global::TestWebservices.localhost.ExceptionTypes.soap);
            }
            catch (System.Web.Services.Protocols.SoapException ex)
            {
                log.LogMsg(0, "Soapausnahme: " + ex.Message + " Actor=" + ex.Actor.ToString() + " Code=" + ex.Code.ToString());
            }
        }

Komplexe Parameter und Rückgabewerte

Die übergabe von Werten fundamentaler Datentypen wie int und string ist problemlos möglich. Bei komplexen Datentypen können zusätzliche Schritte notwendig sein.

Strukturen und Klassen

Die bei der Deserialisierung Komplexer Datentypen werden diese mittels Defaultkonstruktor instanziiert werden. Datenstrukturen haben immer einen Defaultkonstruktor. Bei Klassen muß ein Defaultkonstruktor explizit angegeben werden, falls die Klasse vom Defaultkonstruktor verschiedene Konstruktoren besitzt.

Webservices ermöglichen kein Remoting. Folglich müssen alle Typen als MBV Werte übergeben werden (Serializable).

   [Serializable]
    public struct Point
    {
        public double x;
        public double y;
    }

    [Serializable]
    public class Shape {

        public int id;

    }

    [Serializable]
    public class Line : Shape
    {
        public Line()
        {
        }

        public Line(Point a, Point b)
        {
            this.a.x = a.x;
            this.a.y = a.y;

            this.b.x = b.x;
            this.b.y = b.y;
        }
        public Point a = new Point();
        public Point b = new Point();
    }
Arrays und Listen

Arrays und Listen können problemlos übergeben und zurückgegeben werden, solange sie homogen sind. Homogen ist ein Array oder eine Liste, wenn ihre Elemente alle den gleichen Typ haben. Im folgenden Beispiel werden alle Blogs aus dem Blogserver ausgelesen und über eine Webmethode als Liste an einem Clienten übergeben:

   [WebMethod]
    public System.Collections.Generic.List<DMS.BlogServer.BlogEntry> GetAllBlogs()
    {
        try
        {
            DMS.BlogServer blogServer = new DMS.BlogServer();

            return blogServer.GetAllBlogs();
        }
        catch (Exception ex)
        {
            throw new SoapException(ex.Message, SoapException.ServerFaultCode);
        }
        
    }

Zugriff auf die Webmethode über dem Clienten

        private void btnGetBlogs_Click(object sender, EventArgs e)
        {
            try
            {
                BlogServer.BlogWebService webSvr = new global::TestWebservices.BlogServer.BlogWebService();
                dataGridView1.DataSource = webSvr.GetAllBlogs();
            }
            catch (System.Web.Services.Protocols.SoapException ex)
            {
                log.LogMsg(0, "GetBlogs: " + ex.Message);
            }
            
        }

Werden jedoch heterogene Arrays zurückgegeben, dann ist ein zusätzlicher Schritt erforderlich. In diesem Fall muß der Deserialisierer über die möglichen Kandidaten im Array informiert werden. Ohne diese Informationen wird bei der Deserialisierung eine Exception geworfen. Im folgenden Beispiel erzeugt die Webmethode GetDrawing eine Liste aus Linien und Kreisobjekten, und gibt diese zurück.

    [WebMethod]
    [XmlInclude(typeof(Line))]
    [XmlInclude(typeof(Circle))]    
    public List<Shape> GetDrawing()
    {
        List<Shape> dw = new List<Shape>();
        dw.Add(new Circle(new Point(0, 0), 10));
        dw.Add(new Line(new Point(0, 0), new Point(10, 10)));

        return dw;
    }

Webservices mit "Gedächnis"

Die Statusverwaltung aus ASP.NET ist ebenfalls für Webservices gültig (siehe: Prozesse in einer ASP.NET Anwendung).

Allerdings muß die Aufbewahrung der Cookies auf der Clientseite in die eigene Hand genommen werden, da der kein Browswer sondern ein selbstprogrammiertes etwas ist. Dazu werden die Cookies in einem Objekt vom Typ System.Net.CookieContainer im Client zwischengespeichert. Vor jedem Aufruf ist der Eigenschaft CookieContainer der Proxyklasse der CookieContainer zuzuweisen:


Bsp: Status 2- Durch Applikations- und Sitzungsvariablen implementierte Zähler.

Konsumieren von Webservices

Suchen von Webdiensten

Definition

discovery

Unter discovery wird der Prozess des Erkundens von Webdiensten in einem Netz verstanden

Dienstveröffentlichungen mittels UDDI und DISCO

Um Dienste zu nutzen, muß man wissen, wo Sie angeboten werden, und wie sie aufzurufen sind. Unterstützung findet man dabei durch UDDI- Server (Universal Description , Discovery and Integration) bzw. DISCO- Link- Dateien.

UDDI- Server

siehe. VS- Studio

Disco- Datei mit Verweisen auf Dienste

<?xml version="1.0" encoding="utf-8" ?> 
<discovery xmlns="http://schemas.xmlsoap.org/disco/"
           xmlns:disco="http://schemas.xmlsoap.org/disco/scl/">

        <disco:discoveryRef ref="http://localhost/asp-mcsd2/DMSsearch/dmssearch.asmx?DISCO"/>
        <disco:discoveryRef ref="http://localhost/myServices/add/add.asmx?DISCO"/>
        
</discovery>

Proxyklassen


Struktur der Basisklasse




Erstellen von Proxyklassen

In Visual Studio wird die Erstellung von Proxyklassen voll automatisiert durch die Webverweise. Grundlage dafür sind die WSDL- Beschreibungen, die ASP.NET für jeden Webservice automatisch generiert. Eine WSDL- Beschreibung kann stets über den Querystring ?WSDL zu einem Webservice abgerufen werden.

http://localhost:1516/BlogWebSvr/BlogWebService.asmx?WSDL


Asynchroner Aufruf von Webservices


Schützen eines Webservices

Variante 1: Login- Daten über SoapHeader senden

Unabhängig von den Parameter der Dienstmethoden können über einen SoapHeader weiter Daten bei jedem Dienstaufruf übermittel werden. Dazu wird von der Klasse SoapHeader ein neue Klasse abgeleitet, die den neuen SoapHeader darstellt:

public class CLogin : System.Web.Services.Protocols.SoapHeader {
  public string user;
  public string password;
}

In der Webservice- Klasse muß ein Member vom Typ des neuen SoapHeader angelgt werden:

[WebService...]
public class CSaveService : System.Web.Services.WebService {

    public CLogin Login;
    :
}

Die zu sichernde Dienstmethode muß mit dem SoapHeader ausgestattet werden. Dies geschieht über das Attribut WebMethod:

[WebMethod(), SoapHeader("Login")]
public string getMe()
{
  if (Login == null) 
    throw new Exception("Authentifizierung erforderlich");

  if (Login.user == "Waldi" && Login.password=="Wurst")
    return "Sie haben Zugang";
  else
    throw new Exception("Login ungültig");
}

Der Dienst ist damit gesichert.

Aufruf des gesicherten Dienstes über die Proxyklasse

private void btnGet_Click(object sender, System.EventArgs e)
{
  try 
  {
     localhost.SaveService serv = new testSaveService.localhost.SaveService();
     localhost.CLogin login = new testSaveService.localhost.CLogin();

     login.username = tbxUser.Text;
     login.password = tbxPassword.Text;

     serv.SLoginValue = login;

     this.lblResultat.Text = serv.getMe();
   } 
   catch (Exception ex) 
   {
      lblResultat.Text = ex.Message;
   }
}

Variante 2 : Anmelden mit neuer Identität unter Windows

  1. Objekt von System.Net.NetworkCredential instanziieren

  2. NetworkCredential UserName und Passwort zuweisen

  3. Objekt von System.Net.CredentialCache instanziieren. CredentialCache speichert mehrere Anmeldeinformationen


Konfiguration des Webservices

<?xml version="1.0"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
  <system.web>

    <!-- ... -->

  </system.web>
</configuration>

Debugging aktivieren

<compilation debug="true"/>

Tracing aktivieren

<trace enabled="true"/>

Definieren der Zugriffsmethoden

Die möglichen Zugriffsmethoden wie HttpGet oder HttpSoap werden in der web.config Datei wie folgt definiert:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    
  <system.web>
  
        <webServices>
                <protocols>
                        <add name="HttpGet"/>
                        <add name="HttpPost"/>
                      <remove name="HttpSoap"/>
                </protocols>
        </webServices>
        :
  </system.web>

</configuration>

In diesem Fall würden nur die Zugriffsmethoden Get und Post unterstützt.

WSE 3.0

WSE ist die Abkürzung für Web Service Enhancements. Durch WSE wird das .NET Framework wie folgt erweitert:

  1. Vereinfachte Sicherung von Webservices durch deklarative Konfiguration von Authentifizierung, Signierung und Verschlüsselung

  2. Effiziente Übertragung großer binärer Datenmengen

  3. Routing von Webservices zwecks Wartung und Ausfallsicherung

  4. Klassen zum Schreiben von Stand- Alone Webservice Anwendungen, wodurch kein IIS mehr zum Hosten von WS notwendig ist

  5. SOAP Messaging (Webmethoden ohne Response)

Viele Funktionen in WSE sind nach Installation von WSE durch Konfigurationsdateien administrativ einstellbar. Damit sind Webservices nachträglich ohne Reimplementierung um die WSE Funktionen erweiterbar.

Installation von X509 Zertifikaten

Um Soap- Nachrichten zu signieren und zu verschlüsseln, müssen X509 Zertifikate auf dem Client und dem Server installiert werden. Für die authentifizierung muß eine Serverzertifikat angelegt werden, welches auf dem Serverrechner unter

Localmachine\Personal

und auf dem Client unter

CurrentUser\OtherPerson

abgelegt werden muß.

Für Testzwecke können die Zertifikate der Samples von WSE 3.0 genutzt werden. Diese werden mittels

C:\Programme\Microsoft WSE\v3.0\Samples\Setup.bat

automatisch installiert.

Zertifikatstool

WSE 3.0 liegt ein Tool zur Verwaltung von Zertifikaten bei. Webservices benötigen auf dem Server Zugriff auf die Datei mit dem privaten Schlüssel des Zertifikates. Die NTFS- ACL dieser Datei kann komfortabel mit dem Tool eingestellt werden, indem es ins Tool geladen (Open Certificate), und anschließend mittels View Private Key Properties geöffnet wird. Unter Sicherheit muß auf Windows XP der Benutzer ASPNET, oder unter Server 2003 der Benutzer NETZWERKDIENST Vollzugriff haben.

Konfigurationstool

Die administrative Steuerung von WSE durch Konfigurationsdateien wird durch ein spezielles Konfigurationstool, dem WseConfigEditor3.exe, erleichtert. In dieses kann eine web.config Datei eines Webservices geladen und anschließend über eine GUI die Konfiguration der WSE- Funktionen integriert werden.

Konfiguration von Dienst und Client

Mit dem WseConfigEditor3.exe werden Client und Server konfiguriert. Dazu ist über File/Open die entsprechende Web.Config oder App.config Datei zu öffnen:




Integration von WSE

Die zusätzlichen Funktionen von WSE werden durch eine spezielle Basisklassen für die Proxyklasse der Clients, und eine SoapProtocollFactory für die Webservices bereitgestellt.

Spezielle Basisklasse der Proxys auf der Clientseite


Installation einer spezialisierten Classfactory für Soap- Protokolle auf der Serviceseite

Ein Webservice wird auf WSE erweitert, indem das Element <soapServerProtocolFactory> der web.config Datei hinzugefügt wird:

    <webServices>
      <soapServerProtocolFactory type="Microsoft.Web.Services3.WseProtocolFactory,
                                       Microsoft.Web.Services3, Version=3.0.0.0,
                                       Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      
      </soapExtensionImporterTypes>
    </webServices>

Effiziente Übertragung großer Datenblöcke mittels MTOM

MTOM steht für Message Transmission Optimization Mechanism. Große Blöcke binärerer Daten wie z.B. Bilddaten werden direkt in die Soapnachrichten eingebunden. Die auf einfache Soap- Nachrichten angewendeten Signierungs- und Verschlüsselungsalgorithmen werden damit auch auf die Übertragung großer binärer Datenmengen erweitert. Findet keine Signierung oder Verschlüsselung statt, dann werden binärdaten binär übertragen. Andernfalls werden sie zuerst in Textdaten umgewandelt und als Textdaten in die Soap- Nachruichten eingebettet.

Windows Dienst als Webservice Host

Webservices werden gewöhnlich unter ASP.NET auf einem IIS ausgeführt. WSE 3.0 ermöglicht Webdienste durch Programme oder Windowsdienste zu hosten. Jedoch ist dies derzeit nur auf das TCP- Protokoll mit SOAP beschränkt.

Jeder Webservice ist ein SoapReceiver. Die Menge aller Soap-Receiver, die eine Anwendung oder Dienst bereitstellt, müssen mit der Klasse SoapReceivers in WSE 3.0 registriert werden. Dabei wird jedem Receiver eine sog. EndpointReference zugeordnet. Eine EndpointReference ist eine gegenüber WSDL reduzierte Dienstbeschreibung, die im wesentlichen den Uri des Webdienstes definiert.




Wird ein Windows- Dienst als Host gewählt, dann müssen in seine Assembly die WebService- Typen aufgenommen werden (Hinzufügen der Quellcodes vom Webservice zum Windowsdienst). Im Start- Eventhandler des Dienstes muß den sog. SoapReceivers der Typ des Webservices und der Uri, unter dem er erreichbar sein soll, hinzugefügt werden.

        protected override void OnStart(string[] args)
        {
            // TODO: Hier Code hinzufügen, um den Dienst zu starten.

            Uri address = new Uri("soap.tcp://localhost/TestService");
            SoapReceivers.Add(new EndpointReference(address), typeof(Service));

            log.LogMsg(0, "Wse30StandAloneWebservice gestartet");


        }

        protected override void OnStop()
        {
            // TODO: Hier Code zum Ausführen erforderlicher Löschvorgänge zum Anhalten des Dienstes hinzufügen.

            SoapReceivers.Clear();
            log.LogMsg(0, "Wse30StandAloneWebservice gestoppt");
        }



Wenn der Dienst installiert und gestartet wird, ist der Webservice verfügbar. Eine Proxyklasse kann dann auf dem Client z.B. mittels

c:\WseWsdl3 soap.tcp://localhost/SimpleServicePolicy/Service.asmx /type:webClient /l:CS /n:Wse30Services

erstellt werden. WseWsdl3.exe ist ein spezielles wsdl- Tool für WSE3.0 Dienste.

Sichern von Webdiensten durch Verschlüsseln von Soap Nachrichten

Probleme mit WSE 3.0

Fehler

Beschreibung

Workarround

WseWsdl3 erzeugt keine Microsoft.Web.Services3. WebServicesClientProtocol Proxys

Wird ein Proxy für einen WSEService, der in einem Windowsdienst gehostet wird, z.B. durch wsewsdl3 soap.tcp://localhost/TestSvr /type:webClient erstellt, dann ist der Proxy von der Basisklasse System.Web.Services.Protocols.SoapHttpClientProtocol abgeleitet. Eine Verbindung mit dem Webservice über soap.tcp- Protokoll ist mit diesem Proxy nicht möglich

Austauschen der Basisklasse System.Web.Services.Protocols.SoapHttpClientProtocol gegen Microsoft.Web.Services3. WebServicesClientProtocol

Keine Konfigurationstools im Kontextmenü von Visual Studio


Aufruf des Konfigurationstools aus Start/Programme/Microsoft WSE/ConfigurationTool und öffnen der betreffenden Web.config über das Menü Datei

WCF-Service

In WCF wird ein Dienst in der Regel durch eine Schnittstelle definiert, und durch eine Klasse implementiert. Die Schnittstelle wird dabei als Servicecontract bezeichnet.

Der Servicekontract legt die abrufbaren Leistungen eines Dienstes fest. Jede Leistung wird durch eine Mehtode beschrieben, die mit dem Attribut OperationContract gekennzeichnet ist.




Endpunkte

Die Kommunikation zwischen einem Client und einem Server wird in WCF durch Endpunkte definiert. Endpunkte legen fest,

Die Definition von Endpunkten bilden in WCF damit den Kern der Infrastruktur.


Bindungen

Bindungen definieren das Transportprotokoll (z.B. HTTP) und die Kodierung (z.B. Text/XML).

WCF liefert schon eine Reihe vordefinierter Bindungen: