Friday, January 13, 2012

Creating a SOAP/REST – based WCF Service with Custom Binding/Behaviors


Introduction
Ok, basically the title says it all. This post shows a WCF service with a method exposed both as a SOAP-based and REST-based endpoint. For the SOAP-based endpoint I found that there is no binding that satisfy my needs (yep, I am picky!) so I will create a custom binding – explaining what are custom bindings and what is the Channel layer in general.
Also In order to enable logging operations, I will create two custom behaviors – again explaining what are custom behaviors and what is the ServiceModel layer in general.

The code can be downloaded from here: http://www.devlifestyle.net/media/p/1177/download.aspx

So let’s start with the basics. I have an IIS 7 (you can also go with IIS 6 – no Net-based bindings here) hosted WCF service which exposes a single operation. My simple target is to expose this operation on SOAP-based and REST-based endpoints at the same time.
Lets add a twist: for the SOAP-based endpoint I want to use the basicHttpBinding but I want to have the calls authenticated via a username/password however I do not want any on-the-wire security (i.e. encryption) since all calls are inside my intranet and I do not want the overhead of SSL – so in short I want the same basic behavior of good old ASMX services, just using WCF! Now here is the bump: there is no way to do so out of the box in WCF.

Enter Custom Bindings and the Channel layer.

WCF Architecture…the Channel Layer
WCF is made up of two architectural layers: the Service Model and the Channel layers. Service Model is the higher-level abstraction that you deal with most of the time. Most of developers know it by the famous name of “proxy” at the client side (and less commonly by the name of “dispatcher” at the service side). In its most simple form, when acting as the proxy the Service Model converts our “objects” (data contracts) into the proper message format that the Channel layer understands and hands it down to the Channel layer (known commonly as “serialization”); while when acting as the dispatcher the Service Model gets the message from the low level Channel layer and puts it back into the “objectized” format that our code understands (known commonly as “deserialization”).
While we focus on extending the Service Model later when we create the inspectors, let’s focus now on the low-level Channel layer and creating the custom binding.

So, now we know that the Channel layer is the low-level devil that we all hate to get near to. But anytime you deal with WCF bindings, all what you are actually doing is controlling the creation of WCF channel stack! You are just doing it in the higher-level abstraction courtesy of predefined WCF bindings. But what happens when the out of the box bindings are not enough? Simple: you write down your own binding. In order to do so, you must understand some essentials about WCF bindings and the channel stack.
Bindings (simply) produce a set of ordered BindingElement objects at runtime depending on the type of binding you chose. These binding elements form the entire channel stack (thus the Channel layer). Memorizing all the available BindingElements is difficult unless writing custom bindings (and at times custom BindingElements) is your daily work. Luckily this is not my case! Unluckily though, everytime I have to deal with the channel stack I have to pay a visit for MSDN (http://msdn.microsoft.com/en-us/library/aa347793.aspx) to refresh my memory about the BindingElements and what each does.
So once you get yourself familiar with the type of BindingElements, all what is left for you is to create a custom binding that utilizes these elements in a predefined order…and that’s all what you need to know about the (scary) channel stack.
So what is a BindingElement exactly? Simply it’s a processing step. So simply a binding stacks a set of BindingElements on top of each other where a WCF message passes through each step of this stack and “something” happens to the message. For example, a message security binding element provides message encryption (and other stuff), encoding binding element sets message encoding (text, binary, etc…), a transport encoding sets the protocol (TCP, HTTP, HTTPs, etc…)…you got the point.

Custom Binding
So, back to our custom binding implementing. Recall what are my requirements? I just want to have username/password authentication over HTTP and without any SSL. Here is the part of the code that adds up the proper BindingElements:
public override BindingElementCollection CreateBindingElements()
        {
            var res = new BindingElementCollection();

            XmlDictionaryReaderQuotas rqMax = XmlDictionaryReaderQuotas.Max;
            TextMessageEncodingBindingElement textBE = new TextMessageEncodingBindingElement() { MessageVersion = MessageVersion.Soap12WSAddressing10, MaxReadPoolSize = int.MaxValue, MaxWritePoolSize = int.MaxValue };
            rqMax.CopyTo(textBE.ReaderQuotas);
            res.Add(textBE);

            TransportSecurityBindingElement transportSec = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
            transportSec.LocalServiceSettings.MaxClockSkew = new TimeSpan(10, 0, 0); //allow clock difference between client and service
            transportSec.LocalClientSettings.MaxClockSkew = new TimeSpan(10, 0, 0);
           

            res.Add(transportSec);
            res.Add(new AutoSecuredHttpTransportElement() { MaxReceivedMessageSize = int.MaxValue, MaxBufferPoolSize = int.MaxValue, MaxBufferSize = int.MaxValue });
           
            return res;
        }


Once the custom binding is done, I want to expose my SOAP-based endpoint using this new binding instead of the basicHttpBinding. That’s the easy part. Let’s see the service configuration:
First we create a binding extenstion element and supply and custom binding dll:
<extensions>
      <bindingExtensions>
        <add name="customHttpBinding" type="MOHE.CustomHttpBinding.CustomHttpCollectionElement, CustomHttpBinding"/>
      bindingExtensions>
    extensions>

Then we create the binding element itself:
<bindings>
      <customHttpBinding>
        <binding name="myCustomHttpBinding">
        binding>
      customHttpBinding>
    bindings>

Finally, we define our SOAP endpoint using the new binding, as follows:
<services>
      <service name="Service" behaviorConfiguration="ServiceBehavior">
        <endpoint address="soap" binding="customHttpBinding" bindingConfiguration="myCustomHttpBinding" contract="IService" />
      service>
    services>


Needless to say that the binding must also be used at the client side…since now you know what the configuration elements mean you can check the client configuration yourself.

Custom Username/Password Validator
Before wrapping up, while we are in the “customization” mode, lets do one final thing: now that we have a custom binding that supports username/password authentication, lets a create a custom username/password validator and plug that into our service to isolate that authentication logic from the service logic. The code is rather simple:
Public Class UsernameValidator
    Inherits UserNamePasswordValidator

    Public Overrides Sub Validate(ByVal userName As String, ByVal password As String)
        Try
            If userName <> "mohamad" Or password <> "halabi" Then
                Throw New Exception("Unknown Username or Password")
            End If
        Catch ex As IndexOutOfRangeException
            Throw New FaultException("Unknown Username or Password")
        End Try
    End Sub
End Class
Needless to say, I hardcoded my username/password…you will authenticate against your favorite user store; be it SQL, AD, etc…

Again, lets see how to add the custom username/password validator to the WCF config file:
<serviceBehaviors>
        <behavior name="ServiceBehavior">
          <dataContractSerializer maxItemsInObjectGraph="10000000"/>
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceCredentials>
            <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="CustomUsernameValidator.UsernameValidator,
CustomUsernameValidator"/>
          serviceCredentials>
        behavior>
      serviceBehaviors>
The “userNameAuthentication” tag is supplied with the dll holding my custom validation logic.

Just in case you’re wondering about the other configurations, here’s a quick look:
--dataContractSerializer: if you want to parse large messages in WCF (http://thedotnethub.blogspot.com/2009/10/passing-large-messages-in-wcf.html) there are some attributes you want to set. One of those is maxItemsInObjectGraph which sets how many objects can be serialized and passed around.
--serviceMetadata: enables metadata publishing which grants access to the wsdl
--serviceDebug: enables exception details to be propagated to callers; handy in development

REST-enabling the service
So, let’s think about this: we have a WCF-SOAP service that is using an HTTP based binding. What SOAP-features are we exactly using here? Besides SOAP envelopes…nothing! We aren’t using message security, we aren’t using addressing, we aren’t using routing, we aren’t using transactions…in short we aren’t using any of the WSE standards. Frankly, SOAP is an overhead here! Why? try passing an array of – say 500 – objects from a SOAP service to a client. Now pass the same exact objects in REST style. You will find that the size of the REST request is at least 50% less than the size of the SOAP request. Now that’s some saving.
Moral of the story: unless you need SOAP (i.e. any of the WSE standards), REST is the way to go. Its reliance on HTTP verbs makes it perfect for interoperability (SOAP also supports interoperability), it supports caching, and makes it a perfect fit for client-side invoking via JavaScript, jQuery, or Ajax. Even if you need encryption, you can still enable SSL over REST-based services (but you cannot implement the fine-grained message level security that SOAP supports).

So, in our example I will enable the same operation to be invoked REST-style returning the result in JSON format. Why JSON? It’s the easiest to be parsed with client-side calls…although not shown here. Why JSON #2? Although it makes you sound cool to say “POX”, I just like Javascript more.

So, lets see our operation:
[OperationContract]
    [WebInvoke(Method = "POST", UriTemplate = "/PostPerson", ResponseFormat = WebMessageFormat.Json)]
    List<Person> PostPerson(Person person);
The OperationContract is for exposing the operation SOAP-style…what we seen before. The WebInvoke attribute is what enables REST magic. I will be using the “POST” verb; although I can also use the “PUT” verb but I will follow the analogy that POSTs are for inserts while PUTs are for updates. You also have DELETE and GET.

Simply, in the web.config we publish the same contract via a second binding, called webHttpBinding:
<endpoint address="rest" binding="webHttpBinding" contract="IService" />

Now there’s one important point to highlight here: in this example, I am using the webHttpBinding with the REST starter kit dlls (http://msdn.microsoft.com/en-us/netframework/cc950529) to REST-enable WCF. The updated way to do this is using WCF Web API dlls (http://wcf.codeplex.com/wikipage?title=WCF%20HTTP). While I am lazy to update the code, the idea – the concept – behind REST and the method of REST-enabling WCF remains largely the same.

So how do we actually call the REST-enabled operation from the client code? Recall there’s no proxy here, just plain simple HTTP requests. So here is a simple helper method that works with generics to be called from multiple places with different in/out objects.

public static Response Post(string username, string password, RESTService service, string uri, Content content, System.Xml.UniqueId messageId)
{
Response response;
using (HttpClient client = new HttpClient(GetRESTURL(service)))
{
if (string.IsNullOrEmpty(username)) throw new Exception("You must pass username");
if (string.IsNullOrEmpty(password)) throw new Exception("You must pass password");
if (messageId == null) throw new Exception("You must pass messageid");
client.TransportSettings.ConnectionTimeout = new TimeSpan(0, 3, 0);
client.DefaultHeaders.Add("username", username);
client.DefaultHeaders.Add("password", password);
client.DefaultHeaders.Add("clientmachinename", Environment.MachineName);
client.DefaultHeaders.Add("messageid", messageId.ToString());

HttpContent httpContent = HttpContentExtensions.CreateJsonDataContract(content);

using (HttpResponseMessage httpResponse = client.Post(uri, httpContent))
{
if (httpResponse.StatusCode == HttpStatusCode.BadRequest)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(FaultContract));
FaultContract fault = (FaultContract)ser.ReadObject(httpResponse.Content.ReadAsStream());
throw new WebFaultException(fault, HttpStatusCode.BadRequest);
}
else if (httpResponse.StatusCode == HttpStatusCode.ServiceUnavailable)
throw new Exception("ServiceUnavailable");
else
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Response));
response = (Response)ser.ReadObject(httpResponse.Content.ReadAsStream());

return response;
}
}
}

}


WCF Architecture…the Service Layer
Enough easy stuff! Back to the architecture. So recall, that one of the targets of the example is to have logging feature to log every message in and out of the service. Sure we can do this inside the service itself. But this will be neither a good architecture nor cool enough for a post.
So, how do we create a module that catchs every single message into and out from our service, yet we want to do it as a reusable extension without touching our service?

Enter Custom Behaviors and the Service Layer.

Recall that the Service Layer is called “proxy” at the client side and “dispatcher” at the service side. Its job is simply to serialize and deserialize messages to and from the Channel Later respectively. Both the proxy and the dispatcher give you locations – called extensibility locations – that allow you to inject your own logic to inspect messages at various stages and possibly alter the default execution behavior. You can create this logic to inspect messages, operations, parameters, and you can even change serialization & deserialization behavior.
In our example, I am interested in a “message inspector” at the dispatcher side which simply intercepts messages coming in and returning back (requests & responses), and log them into a database. There is one catch here: I want to use the same inspector to log both SOAP and REST messages. SOAP messages are XML-based while REST messages are JSON based so there are some considerations to be taken.
At the proxy side, I also want to create a “client message inspector” to add some metadata to my SOAP message before it is handed on to the channel stack. (note: for variety of configurations, I will enable the client inspector to the SOAP endpoint only).

Message Inspector
Once a message is received, we need to differentiate if it is a SOAP message or a JSON message. Why? because the way to transform the stream into a string format for logging differs:
object IDispatchMessageInspector.AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, 
System.ServiceModel.IClientChannel channel, 
System.ServiceModel.InstanceContext instanceContext)
{
HttpRequestMessageProperty httpReq = (HttpRequestMessageProperty)
request.Properties[HttpRequestMessageProperty.Name];

if (!httpReq.Headers["Content-Type"].Contains("soap"))
return RESTAfterReceiveRequest(ref request, channel, instanceContext);
else
return SOAPAfterReceiveRequest(ref request, channel, instanceContext);
        }


Similarly, before sending the response back we again want to log it:
void IDispatchMessageInspector.BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
            if (reply.Headers.Action == null)
                RESTBeforeSendReply(ref reply, correlationState);
            else
                SOAPBeforeSendReply(ref reply, correlationState);
        }

There are a lot of details in this inspector, such as reading HTTP headers, implementing authentication for REST calls based on these headers, using Task Parallel Libray (TPL) to log data without delaying moving the message down to the channel stack and up to the dispatch operations, and compressing the message before logging it…these are all enhancements however and should lay themselves clear when you download the code.

Now lets see the configuration to add the message inspector:

<extensions>
      <behaviorExtensions>
        <add name="messageInspector" type="MessageInspector.MessageBehaviorExtensionElement, MessageInspector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      behaviorExtensions>
      extensions>

<endpointBehaviors>
        <behavior name="RestBehavior">
          <messageInspector/>
          <webHttp helpEnabled="true" />
        behavior>
        <behavior name="SoapBehavior">
          <messageInspector/>
        behavior>
      endpointBehaviors>
    behaviors>

<services>
      <service name="Service" behaviorConfiguration="ServiceBehavior">
        <endpoint address="rest" binding="webHttpBinding" contract="IService" behaviorConfiguration="RestBehavior" />
        <endpoint address="soap" binding="customHttpBinding" bindingConfiguration="myCustomHttpBinding" contract="IService" behaviorConfiguration="SoapBehavior"/>
      service>
    services>

The inspector is added as a behavior extension…recall this means plugging it in the Service Layer. We then define two endpoint behaviors to add the message inspector to both SOAP and REST endpoints. And finally we update the endpoint definitions so that both endpoints now use the message inspector.

Client Message Inspector

Finally, I also said that I want to add a client inspector at the proxy side to add some metadata to the message. Basically, what I want is simply to log (I know, I have a thing for logging) the name of the machine from where the request is initiated:
object IClientMessageInspector.BeforeSendRequest(ref Message request, IClientChannel channel)
        {
            // Prepare the request message copy to be modified
            MessageBuffer buffer = request.CreateBufferedCopy(int.MaxValue);
            request = buffer.CreateMessage();

            request.Headers.Add(MessageHeader.CreateHeader("MachineName", "http://machinename/CustomHeaders", System.Environment.MachineName));

            return null;
        }
And we then configure our client application to use this inspector for every SOAP message going out:
<extensions>
        <behaviorExtensions>
          <add name="clientMessageInspector" type="ClientMessageInspector.ClientMessageBehaviorExtensionElement, ClientMessageInspector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
        behaviorExtensions>
extensions>

      <behaviors>
        <endpointBehaviors>
          <behavior name="EndpointBehavior">
            <clientMessageInspector/>
            <dataContractSerializer maxItemsInObjectGraph="10000000"/>
          behavior>
        endpointBehaviors>
      behaviors>

<client>
            <endpoint address="http://localhost/WCFService/Service.svc/soap"
                behaviorConfiguration="EndpointBehavior" binding="customHttpBinding"
                bindingConfiguration="myCustomHttpBinding" contract="SOAPRef.IService"
                name="CustomHttpBinding_IService" />
           
        client>










10 comments:

  1. Very interested in seeing the source code for this, but the link doesn't seem to work

    ReplyDelete
    Replies
    1. yes, sorry for that. the old website was replaced and we lost the links...i will reupload the code.

      Delete
  2. Thanks, Nice post, But the download link doesn't work. Please upload.

    ReplyDelete
  3. Please post the code. Thank you.

    ReplyDelete
  4. Greetings! Were you somehow able to fulfill all the settings of your blog all by yourself or you asked for some help?

    ReplyDelete
  5. Dealer's Choice provides various services to the automotive industry that are designed to make the business process more efficient and streamlinedService ContractsOur mission is to reduce the cost of operations for dealerships and increase their productivity with tools that give customer satisfaction a new meaning.....

    ReplyDelete
  6. Hi, nice description in Creating a SOAP based WCF Service with Custom Binding.Thanks for your help..

    -Aparna
    Theosoft

    ReplyDelete
  7. Hi, I was simply checking out this blog and I really admire the premise of the article and this is really informative. I will for sure refer my friends the same. Thanks
    link wheel service

    ReplyDelete
  8. This is really informative and I will for sure refer my friends the samelink wheel services

    ReplyDelete
  9. Nice post. Please, Could you upload de code again? or fix the link...

    THANKS!

    ReplyDelete