This post is part of a series about WCF extensibility points. For a list of all previous posts and planned future ones, go to the index page.
The next behavior in the list is the IEndpointBehavior. Like the other ones, it can be used to inspect and change the endpoint description (which includes the contract description) and its runtime. Unlike the other behaviors, Endpoint behaviors cannot be applied to endpoints as attributes (unless the attribute is applied to an implementation of a callback contract in a duplex pattern – see more details below). Endpoint behaviors are typically used to change the behavior of endpoints in a way which doesn’t interfere with other endpoints (which can potentially be using the same contract) in a service. For example, the WebHttpBehavior changes the runtime to understand the WCF REST attributes (WebGet/WebInvoke).
The pattern continues like the previous posts: Validate is called first, letting the behavior throw if something does not comply with the behavior rules. For example, the WebHttpBehavior (and the WebScriptEnablingBehavior) both throw during validation if the binding does not use HTTP (or HTTPS), or if it is a SOAP binding (i.e., if its message version is not None). AddBuindingParameters is the second one to be called, and it’s not commonly used.
ApplyDispatchBehavior and ApplyClientBehavior are the last ones to be called, after the runtime has been initialized. At that point, the behaviors can update the runtime, updating listeners / dispatchers and other runtime objects according to its logic. The example below will use an endpoint behavior to add a new listener for the endpoint to create a “help page” similar to the WCF Web HTTP Service Help Page.
Via service / endpoint description (server): On the server side, the list of the behaviors for the endpoint can be accessed via the Behaviors property of the endpoint description, which can be obtained once you add an endpoint to the service.
Via the channel factory / generated proxy (client): The ChannelFactory class has an Endpoint property which is of the same type as the one on the server. Likewise, generated proxies (using svcutil / add service reference) are subclasses of ClientBase<T>, which also has an Endpoint property which contains a reference to the list of the endpoint behaviors. Like for contract behaviors, endpoint behaviors need to be added prior to opening the client, otherwise they will not be used.
Using attributes: Endpoint behaviors for “normal” clients / server cannot be applied as attributes – if you define an attribute class and apply it to either the service class, the contract interface or any operations, it will simply be ignored. For duplex endpoints, however, attribute-based endpoint behaviors can be applied to the class which implements the callback contract.
Using configuration: If the endpoint behavior has a configuration extension, then it can be applied directly in the configuration. This works for both service endpoints, and for client endpoints:
One of the nice features added in .NET Framework 4 was a help page for REST endpoints. It described all the operations on the endpoint, as well as the schema for parameters. A few posts on the forums asked about how to customize that page (or the “service” help page), so this is a generic help page implementation (not only for REST endpoints, but for SOAP ones as well). The implementation idea is similar to the one for the POCO Service Host (described in the post about service behaviors): during the call to ApplyDispatchBehavior, the behavior will add a new dispatcher which can handle requests for the help page. Browser clients will then be able to point to that help page, and get a human-readable description of the endpoint.
And before I go any further, here goes the usual disclaimer – this is a sample for illustrating the topic of this post, this is not production-ready code. I tested it for a few contracts and it worked, but I cannot guarantee that it will work for all scenarios (please let me know if you find a bug or something missing). Also, for simplicity sake it doesn’t have a lot of error handling which a production-level code would, and doesn’t support all contract types (asynchronous operations, for example). Finally, this sample (as well as most other samples in this series) uses extensibility points other than the one for this post (e.g., operation invoker, instance providers, etc.) which are necessary to get a realistic scenario going. I’ll briefly describe what they do, and leave to their specific entries a more detailed description of their behavior.
This behavior will also have a customized part of the help page. For this sample, it’s a simple string (the “name of the company” which provides the services), but it can be easily extended to more complex information. Here’s the implementation of IEndpointBehavior. Only ApplyDispatchBehavior will be used here, where we’ll add the new dispatcher to the service. And only if the endpoint address is HTTP – it doesn’t make sense to add a help page for browsers for TCP or named pipes:
CreateChannelDispatcher will create the listener (based on WebHttpBinding, which can handle GET requests natively). The help page will be created by an internal class (HelpPageService) which can handle requests for the help page, and an object of that class will be used as a singleton instance for this dispatcher (since the class is thread-safe, we can share the same instance). Unlike in the service behavior, in which we used the operation behaviors to set up the operation dispatcher properties, this time we need to set them ourselves. So we need to define our invoker and message formatter as well.
Now for the implementation of the helper classes. First the HelpPageService. It’s a very, very simple class which only receives requests for the help page, then returns an instance of a custom Message class.
The HelpPageMessage implements the abstract Message class by overriding the OnWriteBodyContents method. Since WCF messages are XML-based, we’ll use the XML writer passed to the method to write the HTML (as well-formatted XML). The HTML page generated contains a few tables with information about the endpoint obtained from the description, but it’s plain HTML, not too interesting – although it’s useful to notice that the logic to print the operation signature is too simple, as it doesn’t handle out/ref/array/generic parameters correctly (to handle them all this sample would get too big). The interesting item in the message class is the InitializeMessageProperties method. By default, WCF will use the encoder’s ContentType property to determine the Content-Type header of the response, which is usually some XML (or SOAP) type. By adding a HttpResponseMessageProperty to the message, we change the response content type to text/html.
The instance provider will always return the singleton instance of the HelpPageService class – nothing too fancy here. The operation invoker implementation is simple – it will always deliver the incoming Message object to the singleton HelpPageService instance.
Finally, the instance context provider is the same as the one used in the service behavior sample. And the message formatter (converting between Message objects and operation parameters) is trivial, since the operation in the service takes a parameter of that type and returns a value of the same type, so it simply passes them through.
And that’s it. The help page for REST endpoints also contains links to help pages for specific operations and operations schema. To to the same with this example one would need to simply create (one for each new page to be retrieved), or return a different message depending on the request URI.
The last behavior interface (IOperationBehavior).
[Code in this post]
[Back to the index]