Search

Locations of visitors to this page

Categories

On this page

Error handling with WebHttpBinding for Ajax/JSON
Federation over TCP streaming
SOAP message size optimization: Encoding vs compression

Archive

Blogroll

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

RSS 2.0 | Atom 1.0 | CDF

Send mail to the author(s) E-mail

Total Posts: 49
This Year: 5
This Month: 0
This Week: 0
Comments: 69

Sign In

 Tuesday, July 08, 2008
Tuesday, July 08, 2008 2:18:22 PM (GMT Standard Time, UTC+00:00) ( Ajax )

WebHttpBinding is the non-SOAP binding shipped with .net Framework 3.5. This binding along with WebHttpBehavior will be your friend for REST, POX & AJAX scenarios.

When you expose an endpoint using WebHttpBinding the default error handling mechanism is overridden by a custom error handler added by the WebHttpBehavior. Now if you throw a FaultException from your methods – this handler will simply swallow your exception and will generate a generic 400 Bad Request error message. It will do the same for non-fault exceptions. The reason for this change is that in the SOAP world, Faults have a well known wire representation and based on that your FaultException is translated into a SOAP fault message. There is no such wire representation for the faults in REST or AJAX scenarios. If you own both sides (client & service) of the solution, you might want to customize this default WCF behavior. For example, in my scenario I want to send fault object as a JSON string to the client with a 400, “Bad request” http error code. For non-fault exception I will return a generic message with 500 “Internal Server Error”. 

First step of this customization is to subclass the WebHttpBehavior and replace the default error handler with you own error handler.

public class WebHttpBehaviorEx : WebHttpBehavior

{

    protected override void AddServerErrorHandlers(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)

    {

        // clear default erro handlers.

        endpointDispatcher.ChannelDispatcher.ErrorHandlers.Clear();

        // add our own error handler.

        endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new ErrorHandlerEx());

    }

}

Create your own error handler by impelemting IErrorHandler interface.

public class ErrorHandlerEx : IErrorHandler

{

    public bool HandleError(Exception error)

    {

        return true;

    }

    public void ProvideFault(

        Exception error, MessageVersion version, ref Message fault)

    {

     

        //TODO: here we can provide our own fault

    }

}

ProvideFault implementation looks like this:

    public void ProvideFault(

        Exception error, MessageVersion version, ref Message fault)

    {

        if (error is FaultException)

        {

            // extract the our FaultContract object from the exception object.

            var detail = error.GetType().GetProperty("Detail").GetGetMethod().Invoke(error, null);

            // create a fault message containing our FaultContract object

            fault = Message.CreateMessage(version, "", detail, new DataContractJsonSerializer(detail.GetType()));

            // tell WCF to use JSON encoding rather than default XML

            var wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);

            fault.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);

 

            // return custom error code.

            var rmp = new HttpResponseMessageProperty();

            rmp.StatusCode = System.Net.HttpStatusCode.BadRequest;

            // put appropraite description here..

            rmp.StatusDescription = "See fault object for more information.";

            fault.Properties.Add(HttpResponseMessageProperty.Name, rmp);

        }

        else

        {

            fault = Message.CreateMessage(version, "", "An non-fault exception is occured.", new DataContractJsonSerializer(typeof(string)));

            var wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);

            fault.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);

            // return custom error code.

            var rmp = new HttpResponseMessageProperty();

            rmp.StatusCode = System.Net.HttpStatusCode.InternalServerError;

            // put appropraite description here..

            rmp.StatusDescription = "Uknown exception...";

            fault.Properties.Add(HttpResponseMessageProperty.Name, rmp);

        }

    }

 

Now if you throw a FaulException with following fault contract.

    [DataContract]

    public class GreaterThan3Fault

    {

        [DataMember]

        public string FaultMessage { get; set; }

        [DataMember]

        public int ErrorCode { get; set; }

        [DataMember]

        public string Location { get; set; }

    }

You will get this on the client side with a 400 error code. You can use the javascript:eval() to convert this string into javascript object.

{"ErrorCode":90192,"FaultMessage":"Count cannot be greate than 3. Please try again later.","Location":"ISB"}

You can plug-in your custom/extended behavior by using a custom ServiceHostFactory.

 

    public class MyFactory : WebServiceHostFactory

    {

        public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)

        {

            var sh = new ServiceHost(typeof(Service1), baseAddresses);

            sh.Description.Endpoints[0].Behaviors.Add(new WebHttpBehaviorEx());

 

            return sh;

        }

        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)

        {

            return base.CreateServiceHost(serviceType, baseAddresses);

        }

    }

And your svc should look like this.

<%@ ServiceHost Language="C#" Debug="true" Service="Test.Service1" CodeBehind="Service1.svc.cs" Factory="Test.MyFactory" %>

I have attached complete solution with this post.

Update: Please note this sample will not with Microsoft Ajax library as it is. See this post if you are using Microsoft Ajax library as your client.

Download: jsonSerial.zip

Comments [10] | | # 
 Friday, July 04, 2008
Friday, July 04, 2008 2:14:01 AM (GMT Standard Time, UTC+00:00) ( Federation/STS )

Pablo described here a way to configure federation over TCP. In his approach he gets a SAML token from STS and then uses that token to get a security context token which will be used to provide actual message security throughout the session.

As message security only works in a buffered mode, so his approach is not suitable for a TCP streaming scenario. To enable federation along with TCP streaming you have to use mixed mode security (TransportWithMessageCredential) over TCP.  Let’s consider following binding which uses mixed mode security.

      <netTcpBinding>

        <binding name="tcp" transferMode="Streamed">

          <security mode="TransportWithMessageCredential">

            <message clientCredentialType="IssuedToken"/>

            <transport clientCredentialType="Windows"></transport>

          </security>

        </binding>

      </netTcpBinding>

Now the trouble is that there is no way to configure STS settings in this binding configuration so your only choice is to mimic the above settings in a custom binding.

      <wsHttpBinding>

        <binding name="simpTransport">

          <security mode="Transport">

            <transport clientCredentialType="None"/>

          </security>

        </binding>

      </wsHttpBinding>

 

      <customBinding>

        <binding name="tcp">

          <security authenticationMode="SecureConversation">

            <secureConversationBootstrap authenticationMode="IssuedTokenOverTransport">

              <issuedTokenParameters>

                <issuer address="https://localhost:9000/STS" binding="wsHttpBinding" bindingConfiguration="simpTransport"/>

              </issuedTokenParameters>

            </secureConversationBootstrap>

          </security>

          <windowsStreamSecurity/>

          <tcpTransport transferMode="Streamed" />

        </binding>

      </customBinding>

Comments [0] | | # 
 Wednesday, June 18, 2008
Wednesday, June 18, 2008 12:13:13 PM (GMT Standard Time, UTC+00:00) ( WCF )

Sometimes there is a requirement to reduce the size of the messages sent between client and service. Encoder is a WCF component which transforms a SOAP message (Infoset) into a byte stream and vice versa. Kenny has written about the different encoders available in WCF and I highly recommend reading his post.

His post is focused on the performance characteristics of various encoders for various types of messages (with or without binary data).  Here I will talk about other aspect of the encoders. i.e size of the encoded byte stream.

Usually there are few requirements which derive your message size optimization options.

  1. Whether you own both side of communication (client & service) or not?
  2.  Do you require XML(Infoset) representation on the wire?
  3. Deployment options

 Let’s talk about various options in the light of above requirements.

If you own both sides then the recommendation is to use binary encoding which is the default encoding with NetTcpBinding & NetNamedPipeBinding. The XML structure on the wire will not preserved with this encoding however you will get huge savings in message size without writing any additional code.

If byte stream produced by binary encoding still doesn’t meet your bandwidth constraint then you can think of applying compression on this byte stream. There is a compression encoder available as part of WCF samples which you can use as a base to write your own compression encoder. Again this is only useful if wire format is not important to you. 

If you have a scenario in which message flows through intermediaries (e.g IBM DataPower) and certain policies are applied on the message (based on SOAP headers) then above options will not work. In these scenarios you can devise a scheme to only compress SOAP body and replace the actual body with compressed string (quite similar to XML encryption & Digital Signature) while preserving SOAP headers. Pablo has created a protocol (WS-Compression) to achieve this exact requirement. His sample is not updated to the latest version of WCF but you can get the idea by looking at the code. Also you can achieve this same functionality by extending WCF service model layer. For example, by writing a message inspector and doing compression there. 

I did a quick test of various encoding and compression choices and here are my findings. In my tests I have used a large dataset, consisting of 91 customer records, each having 1-20 orders and each order consist of 5-10 products.  

cid:image002.png@01C8D13A.DE114040

Encoding

Size

Bytes

Kb

Standard Text Encoding

1318179

1288

Standard Binary Encoding

259141

253

Gzip Compression using the compressing encoder        

131242

128

Gzip Compression using a custom compression channel

103428

101

 

 

 

 

 

As you can see there is huge size difference between text and binary encoding of xml messages. Note that in my testing there wasn’t any binary data in SOAP messages at all. If there would be some binary data then text encoded messages would be even bigger because of base64 encoding of binary data. So with binary encoding you can save lot of bandwidth without doing any custom coding and that’s why this should be the first choice.

In addition to using a compression encoder you can also use IIS compression if you are hosting in IIS or WPAS (Windows Server 2008). IIS compression is pretty much similar to compression encoder option with the only different that in the former case compression is done by IIS while in later case it is done by a WCF encoder. IIS compression has the disadvantage that you need to configure compression separate to your WCF configuration (IIS metabase).  Using a compression encoder or a custom compression channel also enables you to use a compression algorithm of your choice (based on your compression requirement) while IIS limits you to GZip compression only. In conclusion, the need for xml structure on the wire will be the deciding factor on what option will best suit you.

Comments [0] | | #