Specifying an Endpoint Address
All communication with a Windows Communication Foundation (WCF) service occurs through its endpoints. Each ServiceEndpoint contains an Address, a Binding, and a Contract. The contract specifies which operations are available. The binding specifies how to communicate with the service, and the address specifies where to find the service. Every endpoint must have a unique address. The endpoint address is represented by the EndpointAddress class, which contains a Uniform Resource Identifier (URI) that represents the address of the service, an Identity, which represents the security identity of the service, and a collection of optional Headers. The optional headers provide more detailed addressing information to identify or interact with the endpoint. For example, headers can indicate how to process an incoming message, where the endpoint should send a reply message, or which instance of a service to use to process an incoming message from a particular user when multiple instances are available.
Definition of an Endpoint Address
In WCF, an EndpointAddress models an endpoint reference (EPR) as defined in the WS-Addressing standard.
The address URI for most transports has four parts. For example, this URI, "http://www.fabrikam.com:322/mathservice.svc/secureEndpoint" has the following four parts:
- Scheme: http:
- Machine: www.fabrikam.com
- (optional) Port: 322
- Path: /mathservice.svc/secureEndpoint
Part of the EPR model is that each endpoint reference can carry some reference parameters that add extra identifying information. In WCF, these reference parameters are modeled as instances of the AddressHeader class.
The endpoint address for a service can be specified either imperatively by using code or declaratively through configuration. Defining endpoints in code is usually not practical because the bindings and addresses for a deployed service are typically different from those used while the service is being developed. Generally, it is more practical to define service endpoints using configuration rather than code. Keeping the binding and addressing information out of the code allows them to change without having to recompile and redeploy the application.
There are two ways to specify endpoint addresses for a service in WCF. You can specify an absolute address for each endpoint associated with the service or you can provide a base address for the ServiceHost of a service and then specify an address for each endpoint associated with this service that is defined relative to this base address. You can use each of these procedures to specify the endpoint addresses for a service in either configuration or code. If you do not specify a relative address, the service uses the base address. You can also have multiple base addresses for a service, but each service is allowed only one base address for each transport. If you have multiple endpoints, each of which is configured with a different binding, their addresses must be unique. Endpoints that use the same binding but different contracts can use the same address.
When hosting with IIS, you do not manage the ServiceHost instance yourself. The base address is always the address specified in the .svc file for the service when hosting in IIS. So you must use relative endpoint addresses for IIS-hosted service endpoints. Supplying a fully-qualified endpoint address can lead to errors in the deployment of the service. For more information, see Deploying an Internet Information Services-Hosted WCF Service.
Defining Endpoint Addresses in Configuration
To define an endpoint in a configuration file, use the <endpoint> element.
<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>
When the Open method is called (that is, when the hosting application attempts to start the service), the system looks for a <service> element with a name attribute that specifies "UE.Samples.HelloService". If the <service> element is found, the system loads the specified class and creates endpoints using the endpoint definitions provided in the configuration file. This mechanism allows you to load and start a service with two lines of code while keeping binding and addressing information out of your code. The advantage of this approach is that these changes can be made without having to recompile or redeploy the application.
The optional headers are declared in a <headers> element. The following is an example of the elements used to specify endpoints for a service in a configuration file that distinguishes between two headers: "Gold" clients from http://tempuri1.org/ and "Standard" clients from http://tempuri2.org/. The client calling this service must have the appropriate <headers> element in its configuration file.
<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>
Headers can also be set on individual messages instead of all messages on an endpoint (as shown previously). This is done by using OperationContextScope to create a new context in a client application to add a custom header to the outgoing message, as shown in the following example.
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(); }
Endpoint Address in Metadata
An endpoint address is represented in Web Services Description Language (WSDL) as a WS-Addressing EndpointReference (EPR) element inside the corresponding endpoint's wsdl:port element. The EPR contains the endpoint's address as well as any address properties. Note that the EPR inside wsdl:port replaces soap:Address as seen in the following example.
Defining Endpoint Addresses in Code
An endpoint address can be created in code with the EndpointAddress class. The URI specified for the endpoint address can be a fully-qualified path or a path that is relative to the service's base address. The following code illustrates how to create an instance of the EndpointAddress class and add it to the ServiceHost instance that is hosting the service.
The following example demonstrates how to specify the full endpoint address in code.
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(); }
The following example demonstrates how to add a relative address ("MyService") to the base address of the service host.
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(); }
Note: |
---|
Properties of the ServiceDescription in the service application must not be modified subsequent to the OnOpening method on ServiceHostBase. Some members, such as the Credentials property and the AddServiceEndpoint methods on ServiceHostBase and ServiceHost, throw an exception if modified after that point. Others permit you to modify them, but the result is undefined. Similarly, on the client the ServiceEndpoint values must not be modified after the call to OnOpening on the ChannelFactory. The Credentials property throws an exception if modified after that point. The other client description values can be modified without error, but the result is undefined. Whether for the service or client, it is recommended that you modify the description prior to calling Open. |
Note: |
---|
In Windows7, if the client and the service are on the same machine, the service is running under a UPN account and the client is connecting to the service with the full machine name using 'Negotiate' security mechanism, the call to the service fails with a SecurityNegotiationException ("The target principal name is incorrect"). You can resolve this by either changing the client endpoint address to refer to localhost or specifying the UPN in the request. This is a changed behavior on the Win7 platform as compared to down-level (such as Vista) where the negotiate security mechanism uses Kerberos instead of NTLM.
|
See Also
Reference
EndpointAddressConcepts
Service Identity and AuthenticationEndpoint Creation Overview