Showing posts with label WCF. Show all posts
Showing posts with label WCF. Show all posts

Sunday, January 22, 2017

WCF REST Error Handling And Custom Header

REST, or Representational State Transfer represents an architectural style for building distributed applications. When applied to the world of web services, REST is most commonly used to refer to the transmission of XML over HTTP, and the identification of XML resources via URIs. According to REST, HTTP, XML and URIs provide all the infrastructure for building robust web services, and most developers can therefore safely skip over the pain of learning SOAP and WSDL. If you are new to REST, check out Paul Prescod’s excellent REST articles on xml.com.

A major element of web services is planning for when things go wrong, and propagating error messages back to client applications. However, unlike SOAP, REST-based web services do not have a well-defined convention for returning error messages. In fact, after surveying a number of REST-based web services in the wild, there appear to be four different alternatives for handling errors. Below, I outline the four alternatives, and then provide my opinion on which option or combination of options is best.

Option 1: Stick to HTTP Error Codes

In this scenario, the web service propagates error messages via standard HTTP Error Codes. For example, assume we have the following URL:

http://www.example.com/xml/book?id=1234&dev_token=ABCD1234

This service expects a single parameter: id indicating a book ID. The service extracts the idparameter, does a database look-up and returns an XML representation of the specified book. If the URL specifies an invalid or obsolete id parameter, the service returns an HTTP 404 Not Found Error Code.

Option 2: Return an Empty Set

In this scenario, the web service always returns back an XML document which can have 0 or more subelements. If some error occurs, an XML document with zero elements is returned. The O’Reilly Meerkat news service currently uses this approach. For example, the following URL connects to Meerkat and requests all Linux related articles from the past two days, and formats the results in RSS 0.91:

http://www.oreillynet.com/meerkat/?c=cat10&t=2DAY&_fl=xml

Now, try specifying an invalid category. For example, set c =ten, like this:

http://www.oreillynet.com/meerkat/?c=ten&t=2DAY&_fl=rss


In this case, Meerkat returns an RSS document with zero item elements. This indicates that there are no matching results, but it does not indicate whether this is a valid category ID which contains no news items, or an invalid category ID.

Option 3: Use Extended HTTP Headers

In this scenario, the web service always returns an HTTP 200 OK Status Code, but specifies an application specific error code within a separate HTTP Header. The Distributed Annotation System (DAS) currently uses this alternative approach. For example, the following URL requests sequence data from Human Chromosome 1 from the Ensembl DAS Server:

http://servlet.sanger.ac.uk:8080/das/ensembl1533/sequence?segment=1:100000,100100

Now, try issuing this request for Human Chromosome 30 (there is no human chromosome 30!):

http://servlet.sanger.ac.uk:8080/das/ensembl1533/sequence?segment=30:100000,100100


If you click on the link above, you will see an empty page. However, if you have network sniffer, you can see the following HTTP response:

HTTP/1.1 200 OK
Server: Resin/2.0.5
Content-Encoding: gzip
X-DAS-Version: 1.5
X-DAS-Server: DazzleServer/0.98 (20030508; BioJava 1.3)
X-DAS-Capabilities: dsn/1.0; dna/1.0; types/1.0;
stylesheet/1.0; features/1.0; encoding-dasgff/1.0;
encoding-xff/1.0; entry_points/1.0; error-segment/1.0;
unknown-segment/1.0; component/1.0; sequence/1.0
X-DAS-Status: 403
Content-Type: text/plain
Content-Length: 10
Date: Sun, 30 Nov 2003 21:02:13 GMT


As you can see, the DAS server has returned an HTTP 200 OK Status code and a required X-DAS-Status code. In this case, the code 403 refers to a DAS Specific error code: “Bad reference object (reference sequence unknown)”.

Option 4: Return an XML Error Document

In this scenario, the web service always returns an HTTP Status Code of 200, but also includes an XML document containing an application specific error message. The XooMLeapplication currently uses this approach (XooMLe provides a RESTful API wrapper to the existing SOAP based Google API). For example, the Google API requires that you specify a valid developer token. If you specify an invalid token, XooMLe returns an XML error document. As the XooMLe documentation puts it, “If you do something wrong, XooMLe will tell you in a nice, tasty little XML-package.” For example, try this URL:

http://xoomle.dentedreality.com.au/search/?hl=en&ie=ISO-8859-1&key=&q=oreilly+php

The request does not include a Google developer token. Hence, XooMLe returns back the following XML document:
<?xml version="1.0" ?>
<xoomleError>
 <method>doGoogleSearch</method>
 <errorString>Invalid Google API key supplied</errorString>
 <arguments>
  <hl>en</hl>
  <ie>ISO-8859-1</ie>
  <key></key>
  <q>oreilly php</q>
 </arguments>
</xoomleError>

The Amazon.com web services API also follows this approach, and each returned XML document can specify an ErrorMsg element. For example, see the Amazon.com Dev-Lite Schema.

Best Practices for REST Error Handling

Assuming you are busy implementing a REST-based web service, which error handling option do you choose? I don’t believe there are (yet) any best practices for REST error handling (for an overview of other best REST practices, see Paul Costello’s REST presentation, in particular [Side 59].)

Nonetheless, here are my votes for most important criteria:


  • Human Readable Error Messages: Part of the major appeal of REST based web services is that you can open any browser, type in the right URL, and see an immediate response — no special tools needed. However, HTTP error codes do not always provide enough information. For example, if we take option 1 above, and request and invalid book ID, we get back a 404 Error Code. From the developer perspective, have we actually typed in the wrong host name, or an invalid book ID? It’s not immediately clear. In Option 3 (DAS), we get back a blank page with no information. To view the actual error code, you need to run a network sniffer, or point your browser through a proxy. For all these reasons, I think Option 4 has a lot to offer. It significantly lowers the barrier for new developers, and enables all information related to a web service to be directly viewable within a web browser.
  • Application Specific Errors: Option 1 has the disadvantage of not being directly viewable within a browser. It also has the additional disadvantage of mapping all HTTP error codes to application specific error codes. HTTP status codes are specific to document retrieval and posting, and these may not map directly to your application domain. For example, one of the DAS error codes relates to invalid genomic coordinates (sequence coordinate is out of bounds/invalid). What HTTP error code would we map to in this case?
  • Machine Readable Error Codes: As a third criteria, error codes should be easily readable by other applications. For example, the XooMLe application returns back only human readable error messages, e.g. “Invalid Google API key supplied”. An application parsing a XooMLe response would have to search for this specific error message, and this can be notoriously brittle — for example, the XooMLe server might simply change the message to “Invalid Key Supplied”. Error codes, such as those provided by DAS are important for programmatic control, and easy creation of exceptions. For example, if XooMLe returned a 1001 error code, a client application could do a quick lookup and immediately throw an InvalidKeyException.

Based on these three criteria, here’s my vote for best error handling option:

  • Use HTTP Status Codes for problems specifically related to HTTP, and not specifically related to your web service.
  • When an error occurs, always return an XML document detailing the error.
  • Make sure the XML error document contains both an error code, and a human readable error message. For example:

    <?xml version="1.0" encoding="UTF-8" ?>
    <error>
      <error_code>1001</error_code>
      <error_msg>Invalid Google API key supplied</error_msg>
    </error>
    


By following these three simple practices, you can make it significantly easier for others to interface with your service, and react when things go wrong. New developers can easily see valid and invalid requests via a simple web browser, and programs can easily (and more robustly) extract error codes and act appropriately.
What is the best option for propagating error messages from REST-based web services? Are there other options beyond the four described here?


Create Custom Header  
Reference : Adding Custom Http Header To All WCF Requests

Monday, April 15, 2013

GzipStream To Byte Array

public static string Compress(this string s)
    {
        byte[] bytesToEncode = Encoding.UTF8.GetBytes(s);
        return Convert.ToBase64String(bytesToEncode.Compress());
    }

public static byte[] Compress(this byte[] bytesToEncode)
    {
        using (MemoryStream input = new MemoryStream(bytesToEncode))
        using (MemoryStream output = new MemoryStream())
        {
            using (System.IO.Compression.GZipStream zip = new System.IO.Compression.GZipStream(output, System.IO.Compression.CompressionMode.Compress))
            {
                input.CopyTo(zip);
            }
            return output.ToArray();
        }
    }



public static string Explode(this string s)
    {
        byte[] compressedBytes = Convert.FromBase64String(s);
        return Encoding.UTF8.GetString(compressedBytes.Explode());
    }

public static byte[] Explode(this byte[] compressedBytes)
    {
        using (MemoryStream input = new MemoryStream(compressedBytes))
        using (MemoryStream output = new MemoryStream())
        {
            using (System.IO.Compression.GZipStream zip = new System.IO.Compression.GZipStream(input, System.IO.Compression.CompressionMode.Decompress))
            {
                zip.CopyTo(output);
            }
            return output.ToArray();
        }
    }

Thursday, October 4, 2012

WCF REST: Get the requested URL

Finding the Request.Url is a lot different from standard web work when working in a WCF REST context. It can be quite difficult to find the URL of the request in your WCF REST service.

?
var context = OperationContext.Current;
var requestedUrl =  context.IncomingMessageHeaders.To.PathAndQuery;


Tuesday, September 18, 2012

REST and HTTP Status Codes


REST and HTTP Status Codes

One of the key values of REST is that you are using a universal API known as HTTP.  Because of this you must try to understand the way that HTTP wants you to deal with this.  And how does HTTP want me to deal with this issue?  By using an HTTP Status code.
The first line of an HTTP response is the status line – HTTP clients look to this line to understand the outcome of their request.  It isn’t just a matter of errors, there are many different response codes you could return or encounter when invoking RESTful services.    (For more information on the Status-Line see the HTTP spec, section 6.1)
       Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
The status code comes from a long list of codes, according to the spec, the list of codes is extensible but you should follow the model based on the categories of errors
      - 1xx: Informational - Request received, continuing process
      - 2xx: Success - The action was successfully received,
        understood, and accepted
      - 3xx: Redirection - Further action must be taken in order to
        complete the request
      - 4xx: Client Error - The request contains bad syntax or cannot
        be fulfilled
      - 5xx: Server Error - The server failed to fulfill an apparently
        valid request
Even so, I would not go about creating custom HTTP status codes.  Instead I would try to use the commonly used status codes listed in section 10 of the HTTP spec.

REST equivalent of SOAP faults

The designers of SOAP had exceptions in mind when they created SOAP faults.  Previous distributed computing environments (DCOM, CORBA etc.) did not have a model for propagating exceptions across boundaries.  SOAP came along 10+ years after these technologies a time during which exceptions became accepted as a better way of dealing with errors.  Many SOAP stacks (WCF being the prime example) will catch an exception, turn it into a SOAP fault and pass it along to the client side which will unpack the message and throw an exception on the client side.
SOAP faults are different than HTTP status codes because they are always errors and there is no taxonomy of errors, just whatever you want them to be.  In other words, the “error protocol” is application specific.
In REST HTTP status codes are common and they are not always errors.  However the errors do fall into 2 categories 4XX (Client) and 5XX server errors.   Now let’s consider some common scenarios where you must deal with errors and status codes.

Error Scenarios

The first class of errors you must consider are (4XX) client request errors.  Generally it means that the server rejected the request for some reason.

Get or Update record that does not exist

Client does an HTTP GET for a resource ID that does not exist in the database.  On the server you might call into the data layer to update a record an get a KeyNotFoundException (like I did in the video).  What happens if you do nothing?  Well WCF will catch all unhandled exceptions and return a status code of 500 - “Internal Server Error”.  This is not what you want, because it implies that the problem is a server problem. 
In this case you should return a HTTP status code of 400 404 “Not Found”  (edit – 3/25 – thanks Clemens!)
10.4.1 400 Bad Request
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.
Here is some code that processes a GET request by looking it up in a dictionary.  This is the fixed version of what I showed in the video.  As you can see I’m converting an exception into a status code by throwing a WebProtocolException.
[OperationContract]
[WebGet(UriTemplate = "/{id}")]
SessionData GetSession(string id)
{
    try
    {
        return _sessions[id];
    }
    catch (KeyNotFoundException)
    {
        throw new WebProtocolException(
            HttpStatusCode.NotFound);
    }
}

Add a resource with missing or invalid data

Client does an HTTP POST with missing or invalid data in the request body.  The server attempts to validate the request and rejects it because of the invalid or missing data.  The HTTP status code should be 400 – Bad request

Server Processing Errors

Suppose that the request is valid, the resource exists and for whatever reason (concurrency or some other processing error) the server cannot complete the request.  Then what?
10.5 Server Error 5xx
Response status codes beginning with the digit "5" indicate cases in which the server is aware that it has erred or is incapable of performing the request. Except when responding to a HEAD request, the server SHOULD include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition. User agents SHOULD display any included entity to the user. These response codes are applicable to any request method.
You should return a HTTP status code of 500 along with some text to let the user know what is happening (if you want to).  Here is an example of code that would do this kind of processing. 
[OperationContract]
[WebInvoke(Method = "PUT", UriTemplate = "/{id}")]
SessionData UpdateSession(string id, SessionData updatedSession)
{
    try
    {
        if (!_sessions.Contains(updatedSession.ID))
            throw new WebProtocolException(HttpStatusCode.BadRequest);

        if (!_sessions.IsValidSession(updatedSession))
            throw new WebProtocolException(HttpStatusCode.BadRequest);

        return _sessions.Update(updatedSession);
    }
    catch (TimeoutException)
    {
        throw new WebProtocolException(HttpStatusCode.InternalServerError,"Update session timeout",null);
    }
}
 
 
Source Reference 

Friday, September 14, 2012

Enable WCF tracing



Tracing mechanism in Windows Communication Foundation is based on the classes that resides in System.Diagnostic namespace.Important classes are Trace, TraceSource and TraceListener.

Following are the steps to enable tracing in WCF:

1. Configuring WCF to emit tracing information/Define Trace Source, we have the following options:
  • System.ServiceModel 
  • System.ServiceModel.MessageLogging 
  • System.ServiceModel.IdentityModel
  • System.ServiceModel.Activation 
  • System.Runtime.Serialization 
  • System.IO.Log
  • Cardspace
      In configuration file, we will define a source to enable this configuration as follows:
      <source name="System.ServiceModel.MessageLogging">
2. Setting Tracing Level, we have the following available options, we need to set this tracing level to available options other than default "Off":
  • Off
  • Critical
  • Error
  • Warning
  • Information
  • Verbose
  • ActivityTracing
  • All
      In configuration file, we can choose above values for switchValue attribute as follows:
      <source name="System.ServiceModel.MessageLogging"
                               switchValue=”Information”>

3. Configuring a trace listener
    For configuring a trace listener, we will add following to config file.
     <listeners>
                 <add name="messages"
                 type="System.Diagnostics.XmlWriterTraceListener"
                 initializeData="d:\logs\messages.svclog" />
      </listeners>

4. Enabling message logging
      <system.serviceModel>
         <diagnostics>
            <messageLogging
                    logEntireMessage="true"
                    logMalformedMessages="false"
                    logMessagesAtServiceLevel="true"
                    logMessagesAtTransportLevel="false"
                    maxMessagesToLog="3000"
                    maxSizeOfMessageToLog="2000"/>
         </diagnostics>
    </system.serviceModel>
logEntireMessage: By default, only the message header is logged but if we set it to true, entire message including message header as well as body will be logged.
logMalformedMessages: this option log messages those are rejected by WCF stack at any stage are known as malformed messages.
logMessagesAtServiceLevel: messages those are about to enter or leave user code.
logMessagesAtTransportLevel: messages those are about to encode or decode.
maxMessagesToLog: maximum quota for messages. Default value is 10000.
maxSizeOfMessageToLog: message size in bytes.

Putting all this together, configuration file will appear like this.
--------------------------------------------------------------------------- 
    <system.diagnostics>
    <sources>
      <source name="System.ServiceModel.MessageLogging">
        <listeners>
          <add name="messagelistener"
               type="System.Diagnostics.XmlWriterTraceListener" initializeData="d:\logs\myMessages.svclog"></add>
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
    <system.serviceModel>       
      <diagnostics>
        <messageLogging logEntireMessage="true"
                        logMessagesAtServiceLevel="false"
                        logMessagesAtTransportLevel="false"
                        logMalformedMessages="true"
                        maxMessagesToLog="5000"
                        maxSizeOfMessageToLog="2000">         
        </messageLogging>
      </diagnostics>
    </system.serviceModel>
---------------------------------------------------------------------------

Note: In this case, information will be buffered and not published to file automatically, So, we can set the autoflush property of the trace under sources as follows:
<trace autoflush ="true" />