RESTful Web Services for Resource-Oriented Architectures (ROA) – Part 1 – URI

My previous posts on RESTful web services focused on the nuts and bolts of building a WCF web service with Visual Studio 2010 and consuming that service with client side jQuery. Although that service is RESTful to the extent it follows Richardson’s and Ruby’s first rule of thumb for REST services, it is not resource-oriented. “If the HTTP method doesn’t match the method information, the service isn’t RESTful. If the scoping information isn’t in the URI, the service isn’t resource-oriented.” 1

The original web service strictly uses HTTP verbs POST, GET, PUT and DELETE to accomplish create, read, update and delete actions. The URI used when creating a record is identical to the one used for reading information : http://mywebsite/myservices/JazzArtists.svc/artists. The differences are found in the HTTP method and the message body. When reading the artist records, we perform a GET request to the URI with no data in the body. The following Ajax call retrieves all artists:

    $.ajax({
            type: "GET",
            dataType: "json",
            async: true,
            url: 'http://mywebsite/myservices/JazzArtists.svc/artists',
            error: function(request, status, error) { alert(request.responseText) },
            success: function(data) {
                     //process return data 
            }
        });

To create a new artist record, we do a POST to the same URI and include the artist data in the message body:

    $.ajax({
            type: 'POST',
            dataType: 'json',
            async: true,
            url: 'http://mywebsite/myservices/JazzArtists.svc/artists',
            contentType: "application/json; charset=utf-8",
            data: '{"PersonObject":{"FirstName":"Shirley","LastName":"Horn","Grammys":9}}',
            error: function(request, status, error) { alert(request.responseText) },
            success: function(data) {
                     //process return data
            }
        });

The HTTP method matches the method information. To update this record, a PUT request is made to http://mywebsite/myservices/JazzArtists.svc/artists with the details about which record to update and how in the message body:

var newGrammys = 10;
$.ajax({
            type: 'PUT',
            data: '{"PersonObject":{"FirstName":"Shirley","LastName":"Horn","Grammys":' + newGrammys + '}}',
            dataType: 'json',
            async: true,
            contentType: "application/json; charset=utf-8",
            url: "http://mywebsite/myservices/JazzArtists.svc/artists",
            error: function(request, status, error) { alert(request.responseText) },
            success: function(data) {
               //process return data
            }
        });

We could have created a web service that updated the artist records through a GET request that included method information in the URI, for example, a GET to http://mywebsite/myservices/JazzArtists.svc/artists?action=Add&FName=Shirley&LName=Horn&NewGrammys=10. However, this would not have been a RESTful service since the method (action=Add) does not match the HTTP method (GET).

Since the method information matches the HTTP method for each request, my previous service follows Richard’s and Ruby’s first rule of thumb for RESTfulness; but, this service is not resource-oriented. The first problem lies in the PUT and DELETE requests. The scope is not in the URI which breaks the second rule of thumb. Deleting an artist is accomplished by a DELETE request to http://mywebsite/myservices/JazzArtists.svc/artists with the name of the artist to delete (the scope) contained in the message body.

        $.ajax({
            type: 'DELETE',
            data: '{"PersonObject":{{"FirstName":"Shirley","LastName":"Horn","Grammys":0}}',
            dataType: 'json',
            async: true,
            contentType: "application/json; charset=utf-8",
            url: http://mywebsite/myservices/JazzArtists.svc/artists,
            error: function(request, status, error) { alert(request.responseText) },
            success: function(data) {
               //process results
            }
        });

My new web service corrects this by including the scope in the URI. A DELETE request made to http://mywebsite/myservices/JazzService.svc/artists/127 will remove record 127 from the target data source.

        $.ajax({
            type: 'DELETE',
            dataType: "xml",
            async: true,
            url: 'http://mywebsite/myservices/JazzService.svc/artists/' + artistId;,
            error: function(request, status, error) { alert(request.responseText) },
            success: function(data) { 
                  //process return data
            }
        });

The same modification is made to the PUT method with the edit information included in the body.

var artistId=127;
$.ajax({
            type: 'PUT',
            data: '<Person xmlns="http://schemas.datacontract.org/2004/07/RESTful" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">'
                  + '<ArtistID>0</ArtistID>'
                  + '<FirstName>Ella</FirstName>'
                  + '<Grammys>14</Grammys>'
                  + '<LastName>Fitzgerald</LastName>'
                  + '</Person>',
            dataType: 'xml',
            async: true,
            contentType: "text/xml",
            processData: false,
            url: 'http://mywebsite/myservices/JazzService.svc/artists/' + artistId,
            error: function(request, status, error) { alert(request.responseText) },
            success: function(data) {
                //process return data
            }
        });

Notice that the real artistId is not included in the message body, only in the URI.

We can see the more RESTful, resource-oriented nature of the new service by inspecting its contracts:


    [DataContract]
    public class Person
    {
        [DataMember]
        public int ArtistID { get; set; }

        [DataMember]
        public string FirstName { get; set; }

        [DataMember]
        public string LastName { get; set; }

        [DataMember]
        public int Grammys { get; set; }

        public Person(int artistid, string firstName, string lastName, int grammys)
        {
            this.ArtistID = artistid;
            this.FirstName = firstName;
            this.LastName = lastName;
            this.Grammys = grammys;
        }

    }
    [ServiceContract]
    public interface IJazzService
    {
        [OperationContract]
        [WebInvoke(Method = "GET",
            ResponseFormat = WebMessageFormat.Xml,
            UriTemplate = "artists/{id}")]
        Person GetArtist(string id);

        [OperationContract]
        [WebInvoke(Method = "GET",
            ResponseFormat = WebMessageFormat.Xml,
            UriTemplate = "artists")]
        List<Person> GetAllArtists();


        [OperationContract]
        [WebInvoke(Method = "POST",
            ResponseFormat = WebMessageFormat.Xml,
            RequestFormat = WebMessageFormat.Xml,
            UriTemplate = "artists")]
        bool PostArtist(Person PersonObject);

        [OperationContract]
        [WebInvoke(Method = "DELETE",
            ResponseFormat = WebMessageFormat.Xml,
            RequestFormat = WebMessageFormat.Xml,
            UriTemplate = "artists/{id}")]
        bool DeleteArtist(string id);

        [OperationContract]
        [WebInvoke(Method = "PUT",
            ResponseFormat = WebMessageFormat.Xml,
            RequestFormat = WebMessageFormat.Xml,
            UriTemplate = "artists/{id}")]
        bool PutArtist(Person PersonObject, string id);

    }

The operations on the group of artists as a whole are completed using POST and GET against the service root URI: /JazzService.svc/artists. Reading, updating and deleting of individual artists are accomplished through their own, individual URI: /JazzService.svc/artists/{artistID}. Thus, a URI is exposed for every piece of data (the group and each individual) on which the client is allowed to operate.

Additionally, the URI has no indication of the method being used. It only points to the resource (data). All method information is contained in the HTTP method: POST, GET, PUT, DELETE.

By restricting the method information to the HTTP method, putting the scope information in the URI and exposing a URI for each resource, this new service meets Richardson’s and Ruby’s basic criteria for a RESTful, resource-oriented web sevices. But we aren’t done with the service if we want it to be truly RESTful. In my next REST post, we’ll discuss the REST constraint of Hypermedia as the Engine of Application State (HATEOAS) and how we can add it to our Jazz Artist web service.

You can see the current working example of this service on my development site.


1. Richardson, Leonard and Ruby, Sam. RESTful Web Services. Sebastopol: O’Reilly Media, Inc., 2008. Ebook

Tagged with: , ,
Posted in REST, Web Services
2 comments on “RESTful Web Services for Resource-Oriented Architectures (ROA) – Part 1 – URI
  1. Larry Smith says:

    hi,

    could you please post the project as a download??

    Thanks..

    Larry

Advertisement