The Canonical REST Entity Service

Recently I was doing a review of some .NET 3.5 WCF REST code based on the REST Starter Kit to see what it would take to move it to .NET 4.

The more I looked at the code and thought about it deeply I realized that the mechanics of exposing a service over HTTP are not the hard part.  What is difficult is to get the semantics of HTTP right.  After spending some time reading through the HTTP spec I took a stab at creating the Canonical REST Entity Service

“ca·non·i·cal [ kə nónnik'l ] conforming to general principles: conforming to accepted principles or standard practice'”

What I was after was a set of requirements that I could verify about the way in which a REST Entity Service should behave.  Here is what I came up with.

First of all I learned that Canonical is spelled with 1 n and not two as in “Cannonical” [sic] which I used all over the place in this code so… sorry. 

Watch

Get Microsoft Silverlight

Download

Canonical REST Service (MSDN Code Gallery)

Canonical REST Entity Service URI Map

Base URI: https://tempuri.org/Resource where Resource is the name of the REST collection (i.e. Customers, Orders etc.)

Web Formats XML and JSON are supported for all request/response messages

URI Template HTTP Method Description Status Response Content Response Contains
/{key} GET Gets a resource by key 200 OK Serialized resource
      304 Not Modified Empty
      400 Bad Request Empty or Serialized error message
      404 Not Found Empty
/?skip={skip}&take={take} GET Gets a list of resources starting with skip+1 and returning take 200 OK Serialized resources
      400 Bad Request Empty or Serialized error message
/ POST Adds a resource to the collection 200 OK Serialized resource
      204 No Content Empty
      400 Bad Request Empty or Serialized error message
      409 Conflict Empty or Serialized error message
/{key} PUT Adds or Updates a resource identified by {key}. Some services might not allow add with PUT. If you return the updated resource, return 200. If you don't return 204. 200 OK Updated resource
      204 No Content Empty
      400 Bad Request Empty or Serialized error message
      404 Not Found Empty or Serialized error message
      409 Conflict Empty or Serialized error message
/{key} DELETE Removes the resource identified by {key}. If you return the resource removed, return 200. If you don't return 204. 200 OK Deleted Resource
      204 No Content Empty
      400 Bad Request Empty or Serialized error message
      404 Not Found Empty or Serialized error message
      409 Conflict Empty or Serialized error message

GET TESTS

GET Spec https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

Tests GET /{key}

  1. GET MUST return a resource given a key if the resource with that key exists
  2. GET MUST return 400-BadRequest if the key is invalid
  3. GET MUST return 404-NotFound if the key is not found
  4. GET MUST return 304-NotModified if Conditional GET conditions are met using If-None-Match
  5. GET SHOULD return an ETag header

Tests GET /?skip={skip}&take={take}

  1. GET MUST skip {skip} resources in the collection and return up to {take} resources.
  2. GET MUST return resources starting with the first one when {skip} is not defined
  3. GET MUST return zero resources when {skip} is greater than the number of resources in the collection
  4. GET MUST return 400-BadRequest if {skip} is < 0
  5. GET MUST return zero or more resources when {take} is not provided
  6. GET MUST return 400-BadRequest if {take} is < 0

POST TESTS

POST Spec https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

Tests POST /

  1. POST MUST append a valid resource to the resource collection using a server generated key and return 201 – Created with a location header, entity tag and entity body
  2. POST MUST return 400-Bad Request if the entity is invalid
  3. POST MUST return 409-Conflict if the entity conflicts with another entity
  4. POST MUST ignore writes to entity fields the server considers read only

PUT TESTS

PUT Spec https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

Tests PUT /{key}

  1. PUT MUST Update the entity identified by the URI if it exists and return 200-OK with the modified entity and etag header
  2. PUT MAY Add a new entity using the key provided in the URI and return 201-Created with entity location and etag
  3. PUT MUST respect the Precondition If-Match
  4. PUT MUST be Idempotent
  5. PUT MUST NOT alter the key of the entity so that it does not match the key of the URI
  6. PUT MUST return 400-BadRequest if the entity is invalid
  7. PUT MUST return 400-BadRequest if the key is invalid
  8. PUT MUST ignore writes to entity fields the server considers read only
  9. PUT MUST return 404-NotFound if the server does not allow new entities to be added with PUT

DELETE TESTS

DELETE Spec https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

Tests DELETE /{key}

  1. DELETE SHOULD delete an entity that exists and return 200-OK with the deleted entity or 204-No Content if the response does not include the entity
  2. DELETE SHOULD be idempotent
  3. DELETE SHOULD return with 412-PreconditionFailed if no matching entity for If-Match etag
  4. DELETE SHOULD succeed if matching entity for If-Match etag
  5. DELETE SHOULD succeed if wildcard used in If-Match etag
  6. DELETE SHOULD return 202-Accepted if the request to delete has not been enacted
  7. DELETE SHOULD return 400-BadRequest if the key is invalid