Search

Locations of visitors to this page

Categories

On this page

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: 50
This Year: 6
This Month: 1
This Week: 1
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] | | # 
Related posts:
Monday, March 16, 2009 9:50:19 PM (GMT Standard Time, UTC+00:00)
I have been looking at this example and I downloaded the source.
I am attempting to recreate your example using a BehaviorExtension but I am failing misearably.
I don't currently use the service.svc file I simply have a hosted windows service with endpoints that resemble http://localhost/SomeURL

I am trying to log exceptions using the exceptionlogging application block and I am getting nothing because of webhttpbinding.

Can you help? I've looked around and all articles seem to lead me back here.

Thanks,
manny
Tuesday, March 17, 2009 10:17:08 AM (GMT Standard Time, UTC+00:00)
How are you accessing the service?
WCF client side treats 400 status code as a protocol error and throws a protocol exception. You should be able to test this using browser or System.Net directly.
see this for an example of behavior extension:
http://www.zamd.net/2008/02/10/SettingProtectionLevelFromConfigFile.aspx
Zulfiqar
Tuesday, March 17, 2009 9:27:02 PM (GMT Standard Time, UTC+00:00)
I am accessing it from a console application. I'll take a look at that article.
Wednesday, April 01, 2009 12:47:41 PM (GMT Standard Time, UTC+00:00)
The code sample you gave does not work. A generic error message is passed back to the client.

You should really test your code before you offer it to the world.

John Andrews
Wednesday, April 01, 2009 9:03:10 PM (GMT Standard Time, UTC+00:00)
Hi John,
I think by client you mean a "WCF based client". WCF client side implementation only allows 200 and few other http status codes. So if you are using WCF as client and you are returning(e.g http 400)then WCF client side will simply through a generic protocol exception.

You can easily test this code by using .net HttpWebRequest API directly.
zulfiqar
Thursday, April 02, 2009 12:30:17 PM (GMT Standard Time, UTC+00:00)
Hey Zulfiqar,

By client i mean a browser client (like in your example). Using fiddler i can see the JSON error is passed back to the browser but the JSON message used to store the error details are not passed by the proxy class to the onfailed function to handle. Instead what seems to be a generic JSON error mesage is sent to the onfailed function.

Setting the HTTP status code to 200, allows the error JSON message to get through, but instead proxy class assumes that the ajax call was succesfull - which is not what i want.

My hope was that the WCF service would through a 200 back to the client with the accompanying JSON error message, and that the client caller would call the onFailed function and pass me the JSON error message.....that does not seem to be happenning :(
John Andrews
Thursday, April 02, 2009 4:35:24 PM (GMT Standard Time, UTC+00:00)
The orignal post was focused primarily on WCF extensibility. Microsoft Ajax libary uses a particular error format which is different than the one I created in my sample and that's why it doesn't flow the error object to the callback and rather returns a generic error object.

See following post on how to modify server side to return error object in a format expected by Microsoft Ajax library.

http://www.zamd.net/2009/04/02/ErrorHandlingWithWebHttpBindingAndMicrosoftAjax.aspx

Hope that helps.
Zulfiqar
Thursday, April 02, 2009 4:53:49 PM (GMT Standard Time, UTC+00:00)
Thanks for the speedy responses!! :)

I'll definitely have a look at the new post!!

I forgot to mention, i had the same issue with JQuery, the $.Ajax call would not return the JSON error message sent by the WCF service.
John Andrews
Saturday, April 25, 2009 2:13:17 PM (GMT Standard Time, UTC+00:00)
Mmm... I might be wrong but it doesn't seem to work with POST's rather than GET's. Or i am missing something bleeding obvious(which is entirely possible lol)?
Eelco
Saturday, April 25, 2009 6:18:51 PM (GMT Standard Time, UTC+00:00)
Eleco,
Service contract of my basic service only uses a [WebGet] attribute so POST won't work. If you need to support POST, you have to annotate appropriate contract method(s) with [WebInvoke] attribute.
All comments require the approval of the site owner before being displayed.
Name
E-mail
Home page

Comment (Some html is allowed: a@href@title, strike) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview