Monday, May 4, 2009

Bad Request in WCF REST services

While working with WCF services we tend to fall on FaultException or FaultException class to send the errors occuring on WCF server to the WCF client.
Now this works with most of the bindings on the WCF framework. However if one is using WebHttpBinding in order to expose the service as a REST service the FaultException or FaultException doesn't work. i.e. if you throw this exception from the server the client gets a Bad Request error. Now this is funny and confusing. I was testing my service by sending raw xml in http requests and when i saw a Bad Request Error i was investigating the schema of the request instead of debugging the service.
After checking the schema many times i finally changed the data in the xml and bingo it worked. And that kind of made me understand the any exception on the service results in a Bad Request exception in the client side.
Now this is not the way we'd like our exceptions to go from server to client. We want to send more specific details about what happened so that the client can take appropriate corrective measures.

One way to achieve this is to define a ResponseStatus enum and include a value of enum in the ResponseMessages from the Service Operations. This enum will keep growing as we want to send more details about the exception and then instead of throwing an exception or fault from the service set the responseStatus in the Response message which the client can use in order to take corrective actions. This obviously is an overhead since this is not the way we'd like to communicate exceptions while using Non WebHttpBindings.

Another way could be to use extensibility features of WCF to set the http status code and status description in the http response. We can intercept the response from the service just before it is sent to the client and set the details appropriately. I want to explore this option in detail if possible however for now i'd go with first approach as my customer has asked me to provide a REST service by today evening :).

~Abhishek

5 comments:

Manojkumar said...

I would like to know how you have implemented it. Can you post it?

Abhishek said...

Hi Manoj,

try to set
WebOperationContext.Current.OutgoingResponse.StatusCode
and
WebOperationContext.Current.OutgoingResponse.StatusDescription
on the server side.
On the client side you can check
WebOperationContext.Current.IncomingResponse.StatusCode
and
if WebOperationContext.Current.IncomingResponse.StatusDescription
to take the appropriate decision.
The status code can be set to appropriate http error code based on the kind of error which occured on the server.

Hope this helps...

~Abhishek

Manojkumar said...

Hi,

Thanks for the quick reply

Below is my sample code. I am trying to learn WCF.

Server Side

ServiceContract
public interface ITService
{
OperationContract
FaultContract(typeof(string))
WebInvoke(Method="GET" ,ResponseFormat=WebMessageFormat.Xml ,UriTemplate="/DoUP/{i}")
int DoUpdate(string i);

}


public class TService : ITService
{
int ITService.DoUpdate(string i)
{

WebOperationContext.Current.OutgoingResponse.StatusDescription = "Test";
//throw new FaultException object(WebOperationContext.Current,"Exp");
//throw new FaultException("abc");
//throw new Exception("My Exception");
return 0;
}
}

Client Side

try
{
WebRequest webRequest = WebRequest.Create("http://localhost:1277/TService.svc/doup/sfsdf");
WebResponse webResponse = webRequest.GetResponse();
int i = 0;
}
catch (Exception ex)
{
//throw new Exception(WebOperationContext.Current.IncomingResponse.StatusDescription);
}

Abhishek said...

let's say something went wrong on the server. you can say
WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.InternalServerError;
WebOperationContext.Current.OutgoingResponse.StatusDescription = "Datastore is down. Try again later";
or if someone deleted the resource from the server before you got the update request from the client
WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.Gone;
WebOperationContext.Current.OutgoingResponse.StatusDescription = "Someone deleted the resource you are trying to update";
or if the request is sent by an unauthorized user
WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.Unauthorized;
WebOperationContext.Current.OutgoingResponse.StatusDescription = "You are not authorized for updating this record";
On the client side try and catch the ProtocolException and you'd get this information in the Message property of the exception object....
Hope this helps...

~Abhishek

Bipin said...

On the client side.. I am getting WebOperationContext.Current as null. So I am not able to get status code and status description.

My sample code.. on client side:
try
{
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(currentURI + "GetData/2");
HttpWebResponse wr = req.GetResponse() as HttpWebResponse;

}
catch (WebException we)
{
HttpStatusCode statusCode = WebOperationContext.Current.IncomingResponse.StatusCode;
String exceptionDetail = WebOperationContext.Current.IncomingResponse.StatusDescription;
Console.WriteLine(exceptionDetail);
}


----------------------------
Could anyone please help me out what went wrong..