Angeben einer Endpunktadresse
Die gesamte Kommunikation mit einem WCF-Dienst (Windows Communication Foundation) erfolgt über dessen Endpunkte. Jeder ServiceEndpoint enthält eine Address, eine Binding und einen Contract. Der Vertrag gibt an, welche Vorgänge verfügbar sind. Die Bindung gibt an, wie eine Kommunikation mit dem Dienst stattfindet, und die Adresse gibt an, wo sich der Dienst befindet. Jeder Endpunkt muss eine eindeutige Adresse haben. Die Endpunktadresse wird durch die EndpointAddress-Klasse dargestellt, die einen Uniform Resource Identifier (URI) enthält, der die Adresse des Diensts darstellt, eine Identity, die die Sicherheitsidentität des Diensts darstellt, und eine Auflistung der optionalen Headers. Die optionalen Header stellen zusätzliche, ausführlichere Adressinformationen bereit, um den Endpunkt zu identifizieren oder mit ihm zu interagieren. Die Header können beispielsweise angeben, wie eine eingehende Nachricht zu bearbeiten ist, wohin der Endpunkt eine Antwortnachricht senden sollte, oder welche Instanz eines Diensts für die Bearbeitung einer eingehenden Nachricht verwendet werden soll, wenn mehrere Instanzen verfügbar sind.
Definition einer Endpunktadresse
In WCF modelliert eine EndpointAddress einen Endpunktverweis (Endpoint Reference, EPR), wie im WS-Addressing-Standard definiert.
Der Adress-URI besteht für die meisten Transporte aus vier Teilen. Der URI http://www.fabrikam.com:322/mathservice.svc/secureEndpoint
hat z. B. die folgenden vier Elemente:
Schema: http:
Computer:
www.fabrikam.com
(Optional) Port: 322
Pfad: /mathservice.svc/secureEndpoint
Bestandteil des EPR-Modells ist, dass jeder Endpunktverweis einige Verweisparameter enthalten kann, die weitere identifizierende Informationen liefern. In WCF werden diese Verweisparameter als Instanzen der AddressHeader-Klasse erstellt.
Die Endpunktadresse für einen Dienst kann entweder verbindlich mithilfe von Code oder deklarativ durch die Konfiguration angegeben werden. Die Definition von Endpunkten im Code ist normalerweise nicht geeignet, da die Bindungen und Adressen für einen bereitgestellten Dienst sich in der Regel von denen unterscheiden, die während der Entwicklung des Diensts verwendet werden. Im Allgemeinen ist es praktischer, Dienstendpunkte nicht mit Code, sondern mit Konfiguration zu definieren. Werden die Bindung und die Adressinformationen nicht in den Code integriert, ist eine Änderung ohne Neukompilierung und erneute Bereitstellung der Anwendung möglich. Wenn im Code oder in der Konfiguration keine Endpunkte angegeben sind, fügt die Runtime einen Standardendpunkt für alle Basisadressen in jedem Vertrag hinzu, der vom Dienst implementiert wird.
Es gibt in WCF zwei Möglichkeiten, Endpunktadressen für einen Dienst anzugeben. Sie können eine absolute Adresse für jeden dem Dienst zugeordneten Endpunkt angeben, oder Sie können eine Basisadresse für den ServiceHost eines Diensts bereitstellen und dann eine relativ zu dieser Basisadresse definierte Adresse für jeden dem Dienst zugeordneten Endpunkt angeben. Sie können mit jedem dieser Verfahren entweder im Code oder in der Konfiguration die Endpunktadressen für einen Dienst angeben. Wenn Sie keine relative Adresse angeben, verwendet der Dienst die Basisadresse. Sie können für einen Dienst auch mehrere Adressen angeben, jedoch ist für jeden Dienst nur eine Basisadresse für jeden Transport zulässig. Wenn Sie mehrere Endpunkte haben, von denen jeder mit einer anderen Bindung konfiguriert ist, müssen deren Adressen eindeutig sein. Endpunkte, die die gleiche Bindung, aber verschiedene Verträge verwenden, können die gleiche Adresse verwenden.
Wenn Sie in IIS hosten, verwalten Sie die ServiceHost-Instanz nicht selbst. Für einen in IIS gehosteten Dienst ist die Basisadresse immer die in der SVC-Datei angegebene Adresse. Daher müssen Sie relative Endpunktadressen für IIS-gehostete Dienstendpunkte verwenden. Die Angabe einer voll qualifizierten Endpunktadresse kann zu Fehlern bei der Bereitstellung des Diensts führen. Weitere Informationen finden Sie unter Bereitstellen eines von IIS gehosteten WCF-Diensts.
Definieren von Endpunktadressen in der Konfiguration
Verwenden Sie das <endpoint>-Element, um eine Endpunktadresse in der Konfigurationsdatei zu definieren.
<configuration>
<system.serviceModel>
<services>
<service name="UE.Samples.HelloService"
behaviorConfiguration="HelloServiceBehavior">
<endpoint address="/Address1"
binding="basicHttpBinding"
contract="UE.Samples.IHello"/>
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="HelloServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Wenn die Open-Methode aufgerufen wird (wenn also die Hostanwendung versucht, den Dienst zu starten), sucht das System nach einem <service>-Element mit einem name-Attribut, das „UE.Samples.HelloService“ angibt. Wird das <service>-Element gefunden, lädt das System die angegebene Klasse und erstellt Endpunkte mithilfe der in der Konfigurationsdatei bereitgestellten Endpunktdefinitionen. Dieser Mechanismus erlaubt Ihnen, mit nur zwei Zeilen Code einen Dienst zu laden und zu starten, ohne dass die Bindungs- und Adressierungsinformationen im Code enthalten sein müssen. Der Vorteil dieses Ansatzes zeigt sich darin, dass diese Änderungen ohne Neukompilierung oder erneute Bereitstellung der Anwendung durchgeführt werden können.
Die optionalen Header werden in <headers> deklariert. Das folgende Beispiel veranschaulicht die Elemente, die verwendet werden, um Endpunkte in einer Konfigurationsdatei für einen Dienst anzugeben, der zwischen zwei Headern unterscheidet: „Gold“-Clients von http://tempuri1.org/
und „Standard“-Clients von http://tempuri2.org/
. Der Client, der diesen Dienst aufruft, muss über entsprechende <headers> in seiner Konfigurationsdatei verfügen.
<configuration>
<system.serviceModel>
<services>
<service name="UE.Samples.HelloService"
behaviorConfiguration="HelloServiceBehavior">
<endpoint address="/Address1"
binding="basicHttpBinding"
contract="UE.Samples.IHello">
<headers>
<Member xmlns="http://tempuri1.org/">Gold</Member>
</headers>
</endpoint>
<endpoint address="/Address2"
binding="basicHttpBinding"
contract="UE.Samples.IHello">
<headers>
<Member xmlns="http://tempuri2.org/">Silver</Member>
</headers>
</endpoint>
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="HelloServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Header können auch durch einzelne Nachrichten statt (wie vorher gezeigt) durch alle Nachrichten an einem Endpunkt festgelegt werden. Dies geschieht, indem mithilfe von OperationContextScope ein neuer Kontext in einer Clientanwendung erstellt wird, um der ausgehenden Nachricht einen benutzerdefinierten Header hinzuzufügen. Dies wird im folgenden Beispiel gezeigt.
SampleServiceClient wcfClient = new SampleServiceClient(new InstanceContext(this));
try
{
using (OperationContextScope scope = new OperationContextScope(wcfClient.InnerChannel))
{
MessageHeader header
= MessageHeader.CreateHeader(
"Service-Bound-CustomHeader",
"http://Microsoft.WCF.Documentation",
"Custom Happy Value."
);
OperationContext.Current.OutgoingMessageHeaders.Add(header);
// Making calls.
Console.WriteLine("Enter the greeting to send: ");
string greeting = Console.ReadLine();
//Console.ReadLine();
header = MessageHeader.CreateHeader(
"Service-Bound-OneWayHeader",
"http://Microsoft.WCF.Documentation",
"Different Happy Value."
);
OperationContext.Current.OutgoingMessageHeaders.Add(header);
// One-way
wcfClient.Push(greeting);
this.wait.WaitOne();
// Done with service.
wcfClient.Close();
Console.WriteLine("Done!");
Console.ReadLine();
}
}
catch (TimeoutException timeProblem)
{
Console.WriteLine("The service operation timed out. " + timeProblem.Message);
Console.ReadLine();
wcfClient.Abort();
}
catch (CommunicationException commProblem)
{
Console.WriteLine("There was a communication problem. " + commProblem.Message);
Console.ReadLine();
wcfClient.Abort();
}
Dim wcfClient As New SampleServiceClient(New InstanceContext(Me))
Try
Using scope As New OperationContextScope(wcfClient.InnerChannel)
Dim header As MessageHeader = MessageHeader.CreateHeader("Service-Bound-CustomHeader", _
"http://Microsoft.WCF.Documentation", "Custom Happy Value.")
OperationContext.Current.OutgoingMessageHeaders.Add(header)
' Making calls.
Console.WriteLine("Enter the greeting to send: ")
Dim greeting As String = Console.ReadLine()
'Console.ReadLine();
header = MessageHeader.CreateHeader("Service-Bound-OneWayHeader", _
"http://Microsoft.WCF.Documentation", "Different Happy Value.")
OperationContext.Current.OutgoingMessageHeaders.Add(header)
' One-way
wcfClient.Push(greeting)
Me.wait.WaitOne()
' Done with service.
wcfClient.Close()
Console.WriteLine("Done!")
Console.ReadLine()
End Using
Catch timeProblem As TimeoutException
Console.WriteLine("The service operation timed out. " & timeProblem.Message)
Console.ReadLine()
wcfClient.Abort()
Catch commProblem As CommunicationException
Console.WriteLine("There was a communication problem. " & commProblem.Message)
Console.ReadLine()
wcfClient.Abort()
End Try
Endpunktadresse in Metadaten
Eine Endpunktadresse wird in WSDL (Web Services Description Language) als ein WS-Addressierungs-EndpointReference
(EPR)-Element innerhalb des wsdl:port
-Elements des entsprechenden Endpunkts dargestellt. Der EPR enthält die Endpunktadresse sowie eventuelle Adresseigenschaften. Beachten Sie, dass der EPR in wsdl:port
soap:Address
ersetzt, wie im folgenden Beispiel gezeigt.
Definieren von Endpunktadressen in Code
Eine Endpunktadresse kann mit der EndpointAddress-Klasse im Code erstellt werden. Der für die Endpunktadresse angegebene URI kann ein voll qualifizierter Pfad oder ein zur Basisadresse des Diensts relativer Pfad sein. Das folgende Codebeispiel zeigt, wie eine Instanz der EndpointAddress-Klasse erstellt und der ServiceHost-Instanz, die den Dienst hostet, hinzugefügt wird.
Im folgenden Codebeispiel wird veranschaulicht, wie eine vollständige Endpunktadresse im Code angegeben wird.
Uri baseAddress = new Uri("http://localhost:8000/HelloService");
string address = "http://localhost:8000/HelloService/MyService";
using (ServiceHost serviceHost = new ServiceHost(typeof(HelloService), baseAddress))
{
serviceHost.AddServiceEndpoint(typeof(IHello), new BasicHttpBinding(), address);
serviceHost.Open();
Console.WriteLine("Press <enter> to terminate service");
Console.ReadLine();
serviceHost.Close();
}
Das folgende Codebeispiel zeigt, wie der Basisadresse des Diensthosts eine relative Adresse ("MyService") hinzugefügt wird.
Uri baseAddress = new Uri("http://localhost:8000/HelloService");
using (ServiceHost serviceHost = new ServiceHost(typeof(HelloService), baseAddress))
{
serviceHost.AddServiceEndpoint(typeof(IHello), new BasicHttpBinding(), "MyService");
serviceHost.Open();
Console.WriteLine("Press <enter> to terminate service");
Console.ReadLine();
serviceHost.Close();
}
Hinweis
Eigenschaften von ServiceDescription in der Dienstanwendung dürfen nicht im Anschluss an die OnOpening-Methode auf ServiceHostBase geändert werden. Einige Member, wie die Credentials-Eigenschaft und die AddServiceEndpoint
-Methoden auf ServiceHostBase und ServiceHost, lösen eine Ausnahme aus, wenn eine Änderung nach diesem Punkt stattfindet. Andere Member können geändert werden, wobei das Ergebnis jedoch nicht definiert ist.
Ähnlich verhält es sich mit den ServiceEndpoint-Werten, die auf dem Client nach dem Aufruf von OnOpening auf ChannelFactory nicht geändert werden dürfen. Die Credentials-Eigenschaft löst eine Ausnahme aus, wenn sie nach diesem Punkt geändert wird. Die anderen Clientbeschreibungswerte können ohne Fehler geändert werden, aber das Ergebnis ist nicht definiert.
Sowohl für den Dienst als auch den Client wird empfohlen, die Beschreibung vor dem Aufruf von Open zu ändern.
Verwenden von Standardendpunkten
Wenn im Code oder in der Konfiguration keine Endpunkte angegeben sind, stellt die Runtime Standardendpunkte bereit, indem ein Standardendpunkt für alle Basisadressen in jedem Dienstvertrag, der vom Dienst implementiert wird, hinzugefügt wird. Die Basisadresse kann im Code oder in der Konfiguration angegeben werden, und die Standardendpunkte werden hinzugefügt, wenn Open auf dem ServiceHost aufgerufen wird.
Wenn Endpunkte explizit bereitgestellt werden, können die Standardpunkte dennoch hinzugefügt werden, indem AddDefaultEndpoints auf dem ServiceHost aufgerufen wird, bevor Open aufgerufen wird. Weitere Informationen über Standardendpunkte, Bindungen und Verhalten finden Sie unter Simplified Configuration (Vereinfachte Konfiguration) und Simplified Configuration for WCF Services (Vereinfachte Konfiguration für WCF-Dienste).