Επεξεργασία

Κοινή χρήση μέσω


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. If no endpoints are specified in code or in configuration, then the runtime adds one default endpoint on each base address for each contract implemented by the service.

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>. 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> 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();
}
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

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.

Using Default Endpoints

If no endpoints are specified in code or in configuration then the runtime provides default endpoints by adding one default endpoint on each base address for each service contract implemented by the service. The base address can be specified in code or in configuration, and the default endpoints are added when Open is called on the ServiceHost.

If endpoints are explicitly provided, the default endpoints can still be added by calling AddDefaultEndpoints on the ServiceHost before calling Open. For more information about default endpoints, bindings, and behaviors, see Simplified Configuration and Simplified Configuration for WCF Services.

See also