Dan Rigsby - Coding Up Style

.Net, C#, & Wcf Development

Archive for the 'REST' Category


REST and Max URL Size

Posted by Dan Rigsby on 17th June 2008

I have heard many people say that the definition of REST is to just put everything in the URL.  This isn’t true.  One of the tenants of REST is to “embrace” the URL and use it as much as possible, but moving information to POST data is just as important and often necessary.  I can understand how this can be misunderstood.  Most REST interfaces do take in all values in into the URL to make it easier to navigate the data.  Having to build a POST package can be tedious at times.  So, what if you do want to send larger data to the service?  When should you decide to move away from a pure URL approach and start using POST data in the header?

Browser and WebServer Limits: http://www.boutell.com/newfaq/misc/urllength.html

  • Internet Explorer: 2,083 characters, with no more than 2,048 characters in the path portion of the URL
  • Firefox: 65,536 characters show up, but longer URLs do still work even up past 100,000
  • Safari: > 80,000 characters
  • Opera: > 190,000 characters
  • IIS: 16,384 characters, but is configurable
  • Apache: 4,000 characters

Different things can happen when you go beyond these URL limits depending on the browser and web servers. It could be a “413 (Entity Too Large)” error, “414 (Request-URI Too Long)” error, the URL could be truncated, or it may just work.  However since different things can happen and your service could be consumed by any source, you have to assume the worst could happen.

By looking at just these numbers, it seems you would be pretty safe with a URL that gets up to 2,000 characters.  However, lets look at the W3C standards on this.  The HTTP specification doesn’t really set a size for the length of a URL, but it does recommend being cautious when going above 255. According to Hypertext Transfer Protocol - HTTP/1.1 section 3.2:

Servers ought to be cautious about depending on URI lengths above 255 bytes, because some older client or proxy implementations might not properly support these lengths.

So for the sake of a REST interface you should avoid setting up an interface where there is any possibility of a URL getting beyond 255.  If you are using a GET verb to request a resource via the URL you should be pretty safe.  However POSTing or PUTing an insert or update via a URL may not be the way to go unless the data size is very small.

The most important thing is to keep your interfaces consistent.  You don’t want one operation that allows an insert via the URL and another through POST data.  This just makes things confusing and less discoverable.  Especially since REST has no way to publish metadata about the operations.   You could follow standards such as these:

CRUD Operation HTTP Verb Data Location
Create POST or PUT POST Data
Read GET URL
Update POST or PUT POST Data
Delete DELETE URL

In general this follows “High REST” which is the more pure REST.  This dictates that the HTTP Verbs define the methods to act on the resource.

Ultimately it’s up to you how you implement the REST interface, but stay consistent, be careful about URL limits, and try to follow industry standards.

Posted in REST, Wcf | 1 Comment »

REST Services and Metadata Endpoints in WCF

Posted by Dan Rigsby on 29th May 2008

As mentioned in a previous article, there is no defined a way to get metadata about REST based services using webHttpBinding.  Since REST doesn’t use SOAP, there is no WSDL that defines the service and no built in way to generate metadata about it.  REST operations return back POX or JSON or something else. 

You can still create a mexHttpBinding endpoint which can be used to represent the structure of the data that the REST service understands.  You just cant really use this metadata to build a proxy client based on the REST interface. You don’t need any addition endpoints to expose the WSDL, having just a webHttpBinding endpoint is enough.  You just need to enable httpGet on the serviceMetadata (see here for more detailed information on this):

<serviceBehaviors>
    <behavior name="contactServiceBehavior">
        <serviceMetadata httpGetEnabled="true"/>
    </behavior>
</serviceBehaviors>

One common tactic to give the REST service some kind of documentation, is to have a "Documentation" or "Help" method on your service that returns a an HTML page as a Stream which describes how to work with the REST service (as seen in pictureservices).  This isn’t really metadata that can be consumed to generate a proxy class, but it is informative to other developers and consumers of your REST service.

Example

Lets look at an example of the "Documentation" method on a REST service.  This example is based on a simple contact service that holds a collection of contacts.  Some of the methods are AddContact, GetContact, UpdateContact, etc. The complete interface for the service is:

using System.Collections.Generic;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Syndication;
using System.ServiceModel.Web;

namespace Rigsby.RestDocumentation
{
    [ServiceContract]
    public interface IContactService
    {
        [OperationContract]
        [WebGet(UriTemplate = "help")]
        Stream GetDocumentation();

        [OperationContract]
        [WebGet(UriTemplate = "contacts")]
        List<Contact> GetAllContacts();

        [OperationContract]
        [WebGet(UriTemplate = "contacts/feed?format={format}",
            BodyStyle = WebMessageBodyStyle.Bare)]
        [ServiceKnownType(typeof(Atom10FeedFormatter))]
        [ServiceKnownType(typeof(Rss20FeedFormatter))]
        SyndicationFeedFormatter GetContactsFeed(string format);

        [OperationContract]
        [WebGet(UriTemplate = "contact/{contactId}")]
        Contact GetContact(string contactId);

        [OperationContract]
        [WebGet(UriTemplate = "contact/name/{name}")]
        List<Contact> GetContactByName(string name);

        [OperationContract]
        [WebGet(UriTemplate="contact/name/startswith/{startsWith}")]
        List<Contact> GetContactsWhereNameStartsWith(string startsWith);

        [OperationContract]
        [WebGet(UriTemplate = "contact/name/endsWith/{endsWith}")]
        List<Contact> GetContactsWhereNameEndsWith(string endsWith);

        [OperationContract]
        [WebGet(UriTemplate = "contact/name/contains/{contains}")]
        List<Contact> GetContactsWhereNameContains(string contains);

        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "contact/{name}")]
        void AddContact(string name);

        [OperationContract(Name = "AddFullContact")]
        [WebInvoke(Method = "POST", UriTemplate = "contact")]
        void AddContact(Contact contact);

        [OperationContract]
        [WebInvoke(Method="DELETE", UriTemplate = "contact/{contactId}")]
        int DeleteContact(string contactId);

        [OperationContract]
        [WebInvoke(Method = "PUT", UriTemplate = "contact/{contactId}")]
        void UpdateContact(string contactId, Contact contact);
    }
}

The operation to look at is "GetDocumentation".  This method returns a stream which will be the HTML of the documentation for the service.  The UriTemplate we are using is just "help", so the full path would just be something like "http://localhost:8080/ContactService/help".

[OperationContract]
[WebGet(UriTemplate = "help")]
Stream GetDocumentation();
 
The implementation of this is pretty straight forward.  We basically just create a new StreamWriter, write the contents of our HTML to it, set content type of the outgoing response to "text/html", and return the stream:
 
public Stream GetDocumentation()
{
    MemoryStream stream = new MemoryStream();

    StreamWriter writer = new StreamWriter(stream, Encoding.UTF8);
    writer.Write(Properties.Resources.Documentation);
    writer.Flush();
    stream.Position = 0;

    WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";

    return stream;
}
 
When a user navigates to "http://localhost:8080/ContactService/help", the following HTML page is displayed which documents how to use the REST interface:

restdoc

The HTML for this was manually hacked together, but you could write some process that looks at some custom attributes on the service to build this help file:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
<head>
    <title>Contact Service - Documentation</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <style type="text/css">
        body {margin: 0px;padding: 0px;font-family: Calibri, Tahoma, Arial;}
        .banner {padding: 5px 5px 5px 15px;background-color: Gray;color: White;}
        .content {margin: 15px;}
    </style>
</head>
<body>
    <div class="banner">
        <h1>Contact Service</h1>
    </div>
    <div class="content">
        <h2>Available Feeds</h2>
        <ul>
            <li><b>GET /contacts/feed?format={format}</b> &mdash; Returns a feed of all contacts as "rss" or "atom"</li>
        </ul>
        <h2>Available Endpoints</h2>
        <ul>
            <li><b>GET /help</b> &mdash; Displays the service documentation</li>
            <li><b>GET /contacts</b> &mdash; Returns all contacts</li>
            <li><b>GET /contact/{contactId}</b> &mdash; Returns a contact by contactId</li>
            <li><b>GET /contact/name/{name}</b> &mdash; Returns all contacts with the specified name</li>
            <li><b>GET /contact/name/startswith/{startswith}</b> &mdash; Returns all contacts whose name starts with the specified string</li>
            <li><b>GET /contact/name/endsWith/{endsWith}</b> &mdash; Returns all contacts whose name ends with the specified string</li>
            <li><b>GET /contact/name/contains/{contains}</b> &mdash; Returns all contacts whose name contains the specified string</li>

            <li><b>POST /contact/{name}</b> &mdash; Adds a new contact with the specified name (ignores POST data)</li>
            <li><b>POST /contact</b> &mdash; Adds a new contact with the POST data</li>
            <li><b>DELETE /contact/{contactId}</b> &mdash; Deletes a contact by contactId</li>
            <li><b>PUT /contact/{contactId}</b> &mdash; Updates a contact by contactId</li>
        </ul>
    </div>
</body>
</html>

You can download a complete sample of the code discussed in this article here: http://www.danrigsby.com/Files/Rigsby.RestDocumentation.zip

kick it on DotNetKicks.com

Posted in REST, Wcf | 14 Comments »