Error handling with WebHttpBinding for Ajax/JSON
Posted by zamd on July 8, 2008
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 work with Microsoft Ajax library as it is. See this post if you are using Microsoft Ajax library as your client.
Download: jsonSerial.zip
Simple REST Services in .NET WCF | Ryan's Blog said
[…] Handling: http://zamd.net/2008/07/08/error-handling-with-webhttpbinding-for-ajaxjson/ Read more from Uncategorized Click here to cancel […]
2010 in review « Zulfiqar's weblog said
[…] Error handling with WebHttpBinding for Ajax/JSON July 2008 1 comment […]
Error handling with WebHttpBinding and Microsoft Ajax « Zulfiqar's weblog said
[…] Here I talked about a technique to convert SOAP faults into JSON objects for AJAX scenarios. The post was primarily around using WCF extensibility to send back exception details as JSON object with a particular HTTP status code. The code will only works with native XMLHttpRequest object or with a library which returns bare HTTP response (Ext JS, jQuery etc). […]
Mohd Nizamuddin said
Hi,
I used the suggested code in my scenario, where I am returning JSON response deployed on IIS 7.5 on WIN2008R2 Standard server. My service project is using .NET 3.5 and website, which is hosted on IIS is using NET 4.0.
My application pool is running using .NET 2.0 framework. But when I used above mentioned code, I got an error and by searching the solution on google, I got that the application pool should run using .NET 4.0 framework.
But the problem is that I cannot have 4.0 Framework on the server.
Is there any workaround to hack the things or am I doing wrong things.
Thanks in advance,
Mohd Nizamuddin
zamd said
Hi Mohd,
This code is broken now and someone send me the fixed code which I have uploaded on skydrive. Please download from following link. Hope that helps.
https://eapekq.bay.livefilestore.com/y1pOikKz1Tn3_DV3aI2cN3tmk85hGoU122tvn1khsaHapv9JUxqdyjrQLIgWyxh7Q-O13QW1l1APDtV5_rFeEaBhQ/jsonSerialWithFaults-fixed.zip?download&psid=1
Jaime said
In the latest version of WCF (As of 11/2011) there’s a better way of doing this using WebFaultException. You can use it as follows in your service catch blocks:
throw new WebFaultException(new ServiceErrorDetail(ex), HttpStatusCode.SeeOther);
[DataContract]
public class ServiceErrorDetail
{
public ServiceErrorDetail(Exception ex)
{
Error = ex.Message;
Detail = ex.Source;
}
[DataMember]
public String Error { get; set; }
[DataMember]
public String Detail { get; set; }
}
Jorge Vinagre said
couldn´t have you used a service behaviour to attach/detach this feature?
Anatoly Mironov said
Reblogged this on Share… What? and commented:
Anyone who has tried this in a SharePoint WCF Service?
merothehero said
Thxs a lot .. was very useful 🙂
Preguntón said
any good patterns and practices about FaultExceptions and Wcf Error Handling ?
any good patterns and practices about it ?
An example of handling errors at the client:
try
{
proxy.SomeOperation();
}
catch (FaultException ex)
{
// only if a fault contract was specified
}
catch (FaultException ex)
{
// any other faults
}
catch (CommunicationException ex)
{
// any communication errors?
}