Service Station

WCF Addressing In Depth

Aaron Skonnard

Contents

Endpoints and Addresses
Addressing Fundamentals
IIS Addressing Considerations
Multiple Endpoints and Unique Addresses
Logical vs. Physical Addresses
Addressing Headers
Message Filters
Host Name Comparisons
Conclusion

Windows Communication Foundation provides a flexible model for exposing service endpoints and communicating with them through a variety of different communication protocols. This month, I dive into various addressing details that surround endpoint communication, many of which enable more advanced messaging scenarios.

Endpoints and Addresses

The Windows® Communication Foundation architecture cleanly separates physical communication details from the underlying service implementation. Developers spend most of their time working with familiar Microsoft® .NET Framework code to implement service contracts and to define service behavior. After the implementation is complete, they can decide how to host the service and expose it to consumers.

In order to host a particular service, you create a ServiceHost instance and define a collection of service endpoints. A service endpoint specifies an address, a binding, and a contract to use for communication. Windows Communication Foundation needs this information to build the necessary messaging runtime, also known as the channel stack, and to provide metadata in the form of Web Services Description Language (WSDL) to external consumers upon request.

You can supply service endpoints to a ServiceHost instance through code (see the ServiceEndpoint class) or through entries in the <system.serviceModel> configuration section (see the <endpoint> element). Anything you can accomplish in code, you can also accomplish in configuration and vice-versa. However, supplying endpoints via configuration usually provides greater flexibility for making post-development changes.

The ServiceHost must contain at least one service endpoint before you attempt to open it or the runtime will throw an exception; a service without at least one endpoint wouldn’t be very useful since you’d have no way to interact with it. A service can, however, expose more than one endpoint in order to accommodate multiple contracts or different types of consumers that have a wide range of capabilities or constraints.

When discussing endpoints, most folks tend to focus on the binding and contract while overlooking the endpoint address and the various issues surrounding it. This month, my goal is to buck that trend by uncovering the various addressing techniques available to you.

For a more thorough discussion of the Windows Communication Foundation programming model and how you work with endpoints in either code or the various configuration elements, see my article called "Learn the ABCs of Programming Windows Communication Foundation" in the February 2006 issue of MSDN® Magazine.

Addressing Fundamentals

When you define service endpoints, you have numerous options for specifying the address. You can specify an IP address or a host name, and you can specify a port number or just assume the default port for the transport. And, of course, you can include a specific path in the address (the part of the address that comes after the host/port).

There are two fundamental techniques you can use when specifying the address in Windows Communication Foundation. You can specify an absolute address for each endpoint or you can supply the ServiceHost with a base address and then specify relative paths for each endpoint. Specifying absolute addresses is a little easier to understand, but the base address technique typically makes things easier to manage.

You specify an absolute address by providing the fully qualified address in the endpoint definition as illustrated in Figure 1. In this example, I’ve specified three absolute addresses, but notice that the two HTTP addresses share the same base address of http://localhost:8080/calcservice. Given this situation, you have the option to provide the host with the base address and then use relative addresses when defining the individual endpoints.

Figure 1 Configuring Endpoints

<configuration>
  <system.serviceModel>
    <services>
      <service name=”CalculatorService”>
        <endpoint address=”http://localhost:8080/calcservice”
                  binding=”basicHttpBinding”
                  contract=”ISimpleMath”/>
        <endpoint address=”http://localhost:8080/calcservice/secure”
                  binding=”wsHttpBinding”
                  contract=”ISimpleMath”/>
        <endpoint address=”net.tcp://localhost:8081/calcservice”
                  binding=”netTcpBinding”
                  contract=”ISimpleMath”/>
      </service>
    </services>
  </system.serviceModel>
</configuration>

You can supply one base address per transport when constructing a ServiceHost object, as shown here:

ServiceHost host = new ServiceHost(typeof(CalculatorService),
    // base HTTP address
    new Uri(“http://localhost:8080/calcservice”),
    // base TCP address
    new Uri(“net.tcp://localhost:8081/calcservice”));

It’s also possible to specify the base addresses in the configuration file along with the endpoints themselves. You do this by listing the base addresses within the <host> element for each <service>, like so:

<configuration>
  <system.serviceModel>
    <services>
      <service name=”CalculatorService”>
        <host>
          <baseAddresses>
            <add baseAddress=”http://localhost:8080/calcservice”/>
            <add baseAddress=”net.tcp://localhost:8081/calcservice”/>
          </baseAddresses>
        </host>
        <!-- endpoint definitions follow -->
        ...

Whether you do it in code or in configuration, both techniques accomplish the same thing—they configure the ServiceHost object with a base address for each transport in use. Once the ServiceHost has been configured this way, you can take advantage of relative URIs when defining endpoint addresses, as you see in Figure 2. When you open the ServiceHost, it determines the absolute address to use for each endpoint by resolving any relative addresses against the corresponding base address (the one that matches the binding’s transport).

Figure 2 Using Relative URIs

<configuration>
  <system.serviceModel>
    <services>
      <service name=”CalculatorService”>
        <endpoint binding=”basicHttpBinding”
                  contract=”ISimpleMath”/>
        <endpoint address=”secure”
                  binding=”wsHttpBinding”
                  contract=”ISimpleMath”/>
        <endpoint binding=”netTcpBinding”
                  contract=”ISimpleMath”/>
      </service>
    </services>
  </system.serviceModel>
</configuration>

Notice both the first and the last endpoints omit the "address" attribute, which is the same as leaving it empty. Hence, each of these endpoints will simply use the corresponding base address for its endpoint address; the first endpoint uses the base HTTP address while the last endpoint uses the base TCP address. The second endpoint uses a relative address of "secure", which will be resolved against the base HTTP address (it becomes http://localhost:8080/calcservice/secure).

Even when using base addresses, you can still specify absolute addresses for endpoints when desired. And the absolute addresses don’t have to match up in any way to the corresponding base address if one exists. Each endpoint can specify a wildly different location if you so choose.

The base address technique is mostly a convenience to reduce the number of places you’ll have to make changes when modifying the locations of your endpoints. Windows Communication Foundation also uses the base HTTP address by default to expose metadata when GET retrieval has been enabled (via the <serviceMetadata> behavior). You can, however, change the retrieval location using the behavior’s httpGetUrl property.

Clients have no awareness of the service’s base address and have no need to support something similar on their side of the wire. As a result, you won’t find anything related to base addresses in the client-side object model or the <client> configuration section. Clients simply choose a particular endpoint, which always comes configured with an absolute address, and that absolute address determines the address it will use during transmission.

These addressing fundamentals apply when self-hosting Windows Communication Foundation services (where you create and manage the ServiceHost instance yourself), but there are some special considerations to keep in mind when you host services within IIS.

IIS Addressing Considerations

When hosting in IIS, you don’t have to worry about creating or managing the ServiceHost instance. IIS takes care of this for you behind the scenes. You simply map a .svc endpoint to your service class, configure the service endpoints and behaviors in web.config, and let Windows Communication Foundation manage the process of creating and configuring the ServiceHost instance at runtime.

In this hosting scenario, the base HTTP address is determined by the IIS virtual directory housing the service along with the name of the .svc file. You, as the developer, really have nothing to say about the base address since it’s fully controlled by the IIS addressing scheme. As a result, Windows Communication Foundation happily ignores any base addresses you may specify in web.config in this scenario. If you want to change the base address for your endpoints, you’ll need to move the service to a different IIS virtual directory.

Not only does IIS control the base address, it forces all of your endpoints to actually use the same base address (unlike self-hosting). This means that if you do specify an absolute address for a particular endpoint, it must start with the base address corresponding to the virtual directory or you’ll get an exception. As a result, it really only makes sense to use relative addresses when hosting in IIS. You will need to use relative addresses when exposing more than one endpoint for a particular .svc service (we’ll discuss why you’d want to do this shortly).

Let’s take a look at an example. Suppose you have a file named calc.svc and you place it in a virtual directory that corresponds to http://localhost:8080/calcservice. The base address for this service will be http://localhost:8080/calcservice/calc.svc. Assuming you’ve enabled HTTP GET metadata retrieval, the help page will be exposed at that same address, which you can simply browse to in Internet Explorer.

Now, consider the endpoint configuration found in the virtual directory’s web.config file (in Figure 3). In this case, the address of the first endpoint becomes the same as the base address (http://localhost:8080/calcservice/calc.svc) since I left the endpoint address empty. The address of the second endpoint becomes the combination of the base address appended with "secure", like this: http://localhost:8080/calcservice/calc.svc/secure. And the address of the "mex" endpoint is http://localhost:8080/calcservice/calc.svc/mex. This can seem a bit strange to some people since the relative portion of the address is added to the right of the file name, but you have to remember that calc.svc is part of the base address so it has to work this way.

Figure 3 Sample Config for IIS-Hosted Service

<configuration>
  <system.serviceModel>
    <services>
        <service name=”CalculatorService”>
          <!-- base address determined by IIS virtual directory -->
          <endpoint binding=”basicHttpBinding”
                    contract=”ISimpleMath”/>
          <endpoint address=”secure”
                    binding=”wsHttpBinding”
                    contract=”ISimpleMath”/>
          <endpoint address=”mex”
                    binding=”mexHttpBinding”
                    contract=”IMetadataExchange”/>
        </service>
        ...

Since I’m on the subject of IIS hosting, I’ll also point out that with IIS 5.0 and IIS 6.0, you can only use the various HTTP bindings in your endpoints. In other words, you cannot host a TCP endpoint in an IIS 5.0- or 6.0-hosted service—only HTTP endpoints are supported. This changes with the Windows Process Activation Services (WAS), which ships with Windows Server code-named "Longhorn." WAS makes it possible to host non-HTTP endpoints within IIS using the same .svc techniques discussed above.

Multiple Endpoints and Unique Addresses

There are a few reasons why you might wish to expose multiple endpoints on a particular service. One reason is to expose the same contract using a few different bindings. For example, you may have some consumers that can only deal with WS-I Basic Profile 1.1-compliant services (one binding) and others that can handle the full suite of standards (another binding). Or you may have some internal enterprise consumers that demand binary TCP transmissions for performance reasons (yet another binding). The ability to expose the same contract using different bindings allows you to accommodate all of these consumers at the same time.

When exposing multiple endpoints with different bindings, each endpoint address must be unique. This is because each endpoint requires a different transport listener and channel stack. Consider the service configuration in Figure 4. In this example, all of the endpoints expose the same contract (ISimpleMath) but each one uses a different binding, so each address must be unique. If you modify an endpoint to use the same address as one of the other endpoints, Windows Communication Foundation will throw an exception while opening the ServiceHost.

Figure 4 Sample Configuration Using Unique Addresses

<configuration>
  <system.serviceModel>
    <services>
      <service name=”CalculatorService”>
        <endpoint address=”http://localhost:8080/calcservice”
                  binding=”basicHttpBinding”
                  contract=”ISimpleMath”/>
        <endpoint address=”http://localhost:8080/calcservice/secure”
                  binding=”wsHttpBinding”
                  contract=”ISimpleMath”/>
        <endpoint address=”net.tcp://localhost:8081/calcservice”
                  binding=”netTcpBinding”
                  contract=”ISimpleMath”/>
      </service>
 ...

If, however, multiple endpoints share the same binding, you can use the same address across those endpoints. This approach makes a lot of sense when you need to expose multiple contracts using the same binding, which is another major case in which you need multiple endpoints on a single service. In the following example, the two defined endpoints share the same address, but they expose different contracts:

<configuration>
  <system.serviceModel>
    <services>
      <service name=”CalculatorService”>
        <endpoint address=”http://localhost:8080/calcservice”
                  binding=”wsHttpBinding”
                  contract=”ISimpleMath”/>
        <endpoint address=”http://localhost:8080/calcservice”
                  binding=”wsHttpBinding”
                  contract=”IScientific”/>
</service>
 ...

In this case, both endpoints end up sharing the same transport listener and channel stack (they simply dispatch to a different set of methods). Windows Communication Foundation is smart enough to know that it can create and use a single binding instance for both endpoints to share since both of them specified "wsHttpBinding" for the binding.

If you’re adding endpoints manually in code, and they’re going to share the same address, you’ll need to ensure that you use the same binding instance when calling AddServiceEndpoint. The following code example illustrates how not to do it:

ServiceHost host = new ServiceHost(typeof(CalculatorService));
host.AddServiceEndpoint(
    typeof(ISimpleMath), new WSHttpBinding(), 
    “http://localhost:8080/calc”);
host.AddServiceEndpoint(
    typeof(IScientific), new WSHttpBinding(), 
    “http://localhost:8080/calc”);
host.Open();
...

Even though both endpoints use the same type of binding, I’ve supplied two distinct instances when calling AddServiceEndpoint. If you attempt to run this code, Windows Communication Foundation throws an exception during the second call to AddServiceEndpoint indicating that a binding instance has already been associated with that particular endpoint address. Instead, you should create a single WSHttpBinding instance and supply it to both AddServiceEndpoint calls as shown here:

ServiceHost host = new ServiceHost(typeof(CalculatorService));
WSHttpBinding wsbinding = new WSHttpBinding();
host.AddServiceEndpoint(
    typeof(ISimpleMath), wsbinding, “http://localhost:8080/calc”);
host.AddServiceEndpoint(
    typeof(IScientific), wsbinding, “http://localhost:8080/calc”);
host.Open();
...

Clients don’t need to be concerned with the fact that both of these endpoints are sharing the same address. The client will still be presented with two distinct endpoints and will have to choose one. In this particular example, svcutil.exe actually generates two proxy classes, one for each endpoint, because they expose different contracts. It just so happens that both proxy classes target the same address.

Logical vs. Physical Addresses

In Windows Communication Foundation, each service endpoint actually has two addresses associated with it—a logical address and a physical address. The difference between these addresses is the same as the difference between "To" and "Via" in WS-Addressing. The logical address ("To") is the address that SOAP messages target. The physical address ("Via"), on the other hand, is the actual transport-specific network address that Windows Communication Foundation listens to for messages to arrive. Throughout the object model, Windows Communication Foundation refers to the logical address as "Address" or "Endpoint Address" and the physical address as "ListenUri".

The Windows Communication Foundation channel infrastructure revolves around the physical address since it’s responsible for receiving incoming messages using a particular transport protocol at a specific location. The Windows Communication Foundation dispatcher, on the other hand, is shielded from such networking details and instead focuses on mapping the incoming message to an endpoint, and ultimately to a method call.

When you specify endpoint addresses like I’ve done thus far, you’re actually supplying the logical address. However, the logical address is also used as the physical address unless otherwise specified. The following example illustrates how to output various endpoint details including both the logical and physical addresses:

...
foreach (ServiceEndpoint se in host.Description.Endpoints)
{
    Console.WriteLine(“Endpoint details:”);
    Console.WriteLine(“Logical address: \t{0}”, se.Address);
    Console.WriteLine(“Physical address: \t{0}”, se.ListenUri);
    Console.WriteLine(“Binding: \t{0}”, se.Binding.Name);
    Console.WriteLine(“Contract: \t{0}”, se.Contract.Name);
    Console.WriteLine();
}
...

If you run it against the preceding examples, it will print the same value for both addresses. In most situations, there’s no reason for the logical and physical addresses to be different. Nevertheless, there are situations where you may need to specify a different physical address. You can specify the ListenUri when defining an endpoint in either code or configuration. The following example illustrates how to do it in code:

host.AddServiceEndpoint(
    typeof(ISimpleMath), wsbinding, 
    “urn:calcservice:simplemath”, // logical 
    new Uri(“http://localhost:8080/calc”)); // physical (listenUri)
host.AddServiceEndpoint(
    typeof(IScientific), wsbinding, 
    “urn:calcservice:scientific”, // logical 
    new Uri(“http://localhost:8080/calc”)); // physical (listenUri)

You can accomplish the same thing in configuration using the listenUri attribute, as Figure 5 illustrates. Now when you run the code to print the endpoint details, you should see different values for the logical and physical addresses. In this example, both endpoints share the same physical address (in this case http://localhost:8080/calcservice), but each one has a unique logical address in the form of a URN.

Figure 5 Specifying the ListenUri

<configuration>
  <system.serviceModel>
    <services>
      <service name=”CalculatorService”>
        <endpoint address=”urn:calcservice:simplemath”
                  listenUri=”http://localhost:8080/calcservice” 
                  binding=”wsHttpBinding”
                  contract=”ISimpleMath”/>
        <endpoint address=”urn:calcservice:scientific”
                  listenUri=”http://localhost:8080/calcservice” 
                  binding=”wsHttpBinding”
                  contract=”IScientific”/>
      </service>
      ...

If you need to guarantee that a ListenUri is unique on the machine, you can use the ListenUriMode property and set its value to ListenUriMode.Unique. When you do this, the channel infrastructure will either choose a unique port number (in the case of TCP, without port sharing enabled) or it will append a GUID to the end of the ListenUri address before using it.

It’s worth noting that you can also use a relative address when specifying the ListenUri and Windows Communication Foundation will resolve it against the corresponding base address just like it does for the logical endpoint addresses.

At this point, you may be wondering how clients will interface with a service configured with a different ListenUri address. Clients have no notion of a ListenUri. As far as clients know, there’s a single address for each endpoint. Since the WSDL definition contains the logical address for each endpoint, svcutil.exe embeds the logical address in the client configuration and will use it for transmission by default.

When a service has been configured with a different physical address, you’ll need an out-of-band mechanism to inform clients of the physical address to use. Then clients can instruct Windows Communication Foundation to route outgoing messages via the physical address, as if it were a router or some other type of intermediary. The ClientViaBehavior makes this easy to accomplish, as illustrated here:

SimpleMathClient client = new SimpleMathClient(
    “WSHttpBinding_ISimpleMath”);
client.Endpoint.Behaviors.Add(new ClientViaBehavior(
        new Uri(“http://localhost:8080/calcservice”)));
double sum = client.Add(3, 4);

It’s also possible to apply the ClientViaBehavior within the client’s configuration file as shown in Figure 6. Now, instead of trying to transmit the message to "urn:calcservice:simplemath", the client transmits the message via the same physical address used by the service endpoint. Once the message arrives at the ListenUri, the dispatcher can determine which endpoint to use by inspecting the logical address (the URN).

Figure 6 Applying the ClientViaBehavior in Configuration

<configuration>
  <system.serviceModel>
    <client>
      <endpoint address=”urn:calcservice:simplemath” 
                binding=”wsHttpBinding”
                behaviorConfiguration=”Via”
                contract=”Client.localhost.ISimpleMath”
                name=”WSHttpBinding_ISimpleMath”/>
      ...
    </client>
    <behaviors>
      <endpointBehaviors>
        <behavior name=”Via”>
          <clientVia viaUri=”http://localhost:8080/calcservice”/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    ...

Why would someone want to use these techniques? The answer is simple: routing. Any time you need to transmit messages through an intermediary en route to the ultimate destination, you’ll need to understand the difference between Address and ListenUri and use them appropriately. This is common in certain enterprise scenarios where a central "gateway" is used to handle common tasks such as security, logging, and statistics for all deployed services.

TcpTrace is exactly this type of intermediary—the only difference is it’s used by developers for diagnostic purposes. Let’s say that you want to use TcpTrace to intercept all messages en route to the service but you don’t want to change the service code. You can easily do this by configuring the client with a ClientViaBehavior pointing to the TcpTrace listen address, and presto, you’re done.

There’s another way to accomplish the same thing without requiring the client to use the ClientViaBehavior. When defining the service endpoint, you can use the same address that TcpTrace is listening on for the logical address (since this address shows up in WSDL, it causes the client to send messages to the TcpTrace listen address). Then you specify a different physical address for the endpoint (the ListenUri) and configure TcpTrace to forward to that address.

In this example, TcpTrace is just like any other intermediary performing simple routing. It’s not hard to extrapolate to more sophisticated custom routing scenarios.

Addressing Headers

In order to accommodate even more sophisticated routing and dispatching logic, you may want to annotate SOAP messages with custom addressing headers. Custom addressing headers are carried in SOAP messages alongside the traditional WS-Addressing headers. For example, if an incoming message contains the <premium> membership header, you dispatch to a feature-rich endpoint whereas if it contains the <basic> header, you route to the endpoint with basic functionality.

You may be wondering why you wouldn’t just specify this information as part of the service contract so it shows up in the message body. The issue is that the consumer may not know or have the required information. Rather, the information will need to be supplied by an intermediary en route to the endpoint. For instance, an intermediary looks up the supplied member id, determines the membership level, and adds the appropriate header to the outgoing message.

You can associate custom addressing headers with endpoints to define additional dispatching criteria. When you do this, only messages containing the required addressing headers will be dispatched to the given endpoint.

When clients need to be aware of any required headers, they should also be included in the service’s WSDL definition; addressing headers are typically described within an <EndpointReference> element as reference parameters (the <EndpointReference> is placed within the WSDL <port> element).

Although this may sound a bit complicated, Windows Communication Foundation makes it easy to work with addressing headers. Consider the service configuration in Figure 7. In this example, I’ve defined two endpoints that share the same logical/physical addresses but each exposes a different contract. Each endpoint also requires a specific addressing header. The first one requires the <basic> header while the second requires the <premium> header. Only incoming messages that contain one of these addressing headers will be dispatched to one of these endpoints.

Figure 7 Endpoints with Custom Addressing Headers

<configuration>
  <system.serviceModel>
    <services>
      <service name=”CalculatorService” 
               behaviorConfiguration=”metadata”>
        <host>
          <baseAddresses>
            <add baseAddress=”http://localhost:8080/calcservice”/>
          </baseAddresses>
        </host>
        <endpoint binding=”wsHttpBinding” contract=”ISimpleMath”>
          <headers>
            <basic xmlns=”http://example.org/level”/>
          </headers>
        </endpoint>
        <endpoint binding=”wsHttpBinding” contract=”IScientific”>
          <headers>
            <premium xmlns=”http://example.org/level”/>
          </headers>
        </endpoint>
      </service>
 ...

It’s also possible to specify addressing headers in code by manually creating the AddressHeader object, and supplying it to an EndpointAddress object. Then you can create a new ServiceEndpoint (based on the EndpointAddress) and add it to the host’s Endpoints collection, as shown here:

ServiceHost host = new ServiceHost(typeof(CalculatorService));
AddressHeader header = 
    AddressHeader.CreateAddressHeader(“basic”, 
       “http://example.org/level”, null);
EndpointAddress ea = new EndpointAddress(
    new Uri(“http://localhost:8080/calcservice/foobar”), header);

host.Description.Endpoints.Add(
    new ServiceEndpoint(
        ContractDescription.GetContract(typeof(ISimpleMath)),
        new WSHttpBinding(), ea));
...

If you inspect the WSDL for this particular endpoint configuration, you’ll find the details in the <service> element (see Figure 8). The <port> element contains a <wsa:EndpointReference> element, which in turn contains the required headers as reference parameters. When you run the WSDL through svcutil.exe, you’ll end up with a similar client-side configuration that includes the same <headers> element. Then when you use that particular endpoint, Windows Communication Foundation will automatically include the required <basic> header in the outgoing message, as you see in Figure 9.

Figure 9 Custom Addressing Headers Sent in SOAP

<s:Envelope xmlns:s=”http://www.w3.org/2003/05/soap-envelope” 
            xmlns:a=”http://www.w3.org/2005/08/addressing”>
  <s:Header>
    <a:Action s:mustUnderstand=”1”
     >http://example.org/calc/Add</a:Action>
    <a:MessageID
     >urn:uuid:db1ba7a6-ca2e-494b-b390-9f621e8b90f4</a:MessageID>
    <a:ReplyTo>
      <a:Address
       >http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <a:To s:mustUnderstand=”1”>http://localhost:8080/calcservice</a:To>
    <basic a:IsReferenceParameter=”true” 
      xmlns=”http://example.org/level”/>
  </s:Header>
  <s:Body>
  ...

Figure 8 Custom Addressing Headers in WSDL

...
<wsdl:service name=”CalculatorService”>
  <wsdl:port name=”WSHttpBinding_ISimpleMath” 
             binding=”tns:WSHttpBinding_ISimpleMath”>
    <soap12:address location=”http://localhost:8080/calcservice”/>
    <wsa10:EndpointReference>
      <wsa10:Address>http://localhost:8080/calcservice</wsa10:Address>
      <wsa10:ReferenceParameters>
        <basic xmlns=”http://example.org/level”/>
      </wsa10:ReferenceParameters>
      ...
    </wsa10:EndpointReference>
  </wsdl:port>
  ...
</wsdl:service>
...

You can confirm that the dispatcher is looking for the <basic> header by removing the <headers> element from the generated client configuration and invoking the service again, at which point dispatching will fail.

Message Filters

You can dig down even further to customize the dispatching process by leveraging what are known as message filters. When Windows Communication Foundation dispatches incoming messages, it uses message filters to determine the matching endpoint, if one exists. You can choose which message filter to use or you can provide your own. This flexibility allows you to break free from the traditional dispatching model when using Windows Communication Foundation to implement things other than traditional SOAP—for instance, the techniques described here enable you to implement REST/POX-style services on the Windows Communication Foundation messaging foundation.

Each endpoint actually has two filters associated with it—an address filter and a contract filter. The address filter determines if the incoming message matches the endpoint’s "To" address and any required address headers, while the contract filter determines whether it matches the endpoint’s contract. Both filters are used by the dispatcher to determine the destination endpoint.

A filter is a simply a class that derives from MessageFilter, which defines a few abstract Match methods. There are several built-in implementations of MessageFilter including EndpointAddressMessageFilter and ActionMessageFilter, which are used as the default address and contract filters respectively. The EndpointAddressMessageFilter simply compares the "To" address to the endpoint address and expects an exact match. It also compares the addressing headers found in the incoming message with the collection of addressing headers required by the endpoint. The ActionMessageFilter compares the incoming "Action" value against the actions on the contract, again looking for an exact match.

There are other addressing filters that provide different behavior. For example, the PrefixEndpointAddressMessageFilter causes an incoming "To" address to match the endpoint address as long as it starts with the same address prefix (a looser match). There’s also a MatchAllMessageFilter, which causes all messages to match the given endpoint.

An easy way to change the message filter used by a service is through the AddressFilterMode property of [ServiceBehavior]. AddressFilterMode comes with three values: Any, Exact, and Prefix. Each of these values maps to the corresponding MessageFilter implementation. The following example illustrates how to configure my service to use the PrefixEndpointAddressMessageFilter:

[ServiceBehavior(AddressFilterMode=AddressFilterMode.Prefix)]
public class CalculatorService : ISimpleMath, IScientific
{
   ...

It’s also possible to change each endpoint’s filters by writing some code. You can even write your own MessageFilter implementation to incorporate some customized matching logic. Windows Communication Foundation does come with an XPathMessageFilter, which allows you to evaluate arbitrary XPath expressions against incoming messages during the matching process, single-handedly covering content-driven scenarios.

Host Name Comparisons

One final note about addresses and dispatching. It’s common to hard-code a specific host name into the physical address when defining an endpoint. But what if you want to configure the endpoint to listen for all host names bound to a particular machine (localhost, contoso, www.contoso.com, and so on)? Or what if you want to restrict an endpoint to only match a particular host name?

You can further control this via the HostNameComparisonMode, which is made available on most bindings as a property. HostNameComparisonMode comes with three settings: StrongWildcard (the default), Exact, and WeakWildcard.

StrongWildcard and WeakWildcard mean any hostname plus port combination will match the endpoint address (including IP addresses). StrongWildcard matches simply have higher priority than WeakWildcard matches. Exact means the hostname plus port must exactly match the endpoint address (a case-insensitive match at least).

Since the default mode is StrongWildcard, your endpoints should automatically handle the differences between localhost and your machine name, so you’ll only need to use this feature when you wish to further constrain the matching process.

Note that many of the addressing techniques discussed in this article will only work when used in conjunction with bindings configured to use SOAP 1.2 and WS-Addressing. If you’re having issues getting something to work, make sure you’re using a modern binding like the WSHttpBinding. For more information on how to enable a binding to use WS-Addressing, see last month’s column titled "WCF Messaging Fundamentals".

Conclusion

The Windows Communication Foundation messaging framework provides developers with numerous addressing features that are designed to simplify common hosting and communication scenarios and to increase flexibility when dealing with sophisticated routing and dispatching logic. Adding these addressing techniques to your toolbelt will increase your future productivity, not to mention your skill set for bending Windows Communication Foundation to your will.

Send your questions and comments for Aaron to  sstation@microsoft.com.

Aaron Skonnard is a cofounder of Pluralsight, a Microsoft .NET training provider. Aaron is the author of Pluralsight’s Applied Web Services 2.0, Applied BizTalk Server 2006, and Applied Windows Communication Foundation courses. Aaron has spent years developing courses, speaking at conferences, and teaching professional developers.