Creating a RESTful Web Service Using ASP.Net MVC Part 12 – Creating a Resource With POST

December 5, 2008 00:55 by admin

Now we can create a resource from an incoming XHTML representation, it is time to accept a representation of a new resource via an HTTP POST. This should be pretty much the same as accepting a new resource via an HTTP PUT.

So what is the difference between a PUT request and a POST request? A PUT request is made directly to the URI of the new resource, the client dictates the URI. A POST request is made to the URI of the new resource’s “parent” URI, the server decides on the full URI of new resource.

The standard interface we defined in Part 7 specified that the products resource would accept a new product representation via a POST request. As mentioned in Part 10 there are issues with using POST… every time the server receives a POST request, it will create a new resource. This means that if the client doesn’t receive a response (i.e. it doesn’t know if the resource was actually created or not) it cannot “Safely” re-submit the same request. So whilst for this example web service we will support POST, its use in real life needs to be carefully considered. Examples of where it is useful include fire and forget requests such as non-critical logging of data and resources that must be allocated a URI in a specific sequence managed by the server.

So what changes were made to the web service to support POST? Firstly the ItemXmlExternalToInternal.xslt and ItemXhtmlExternalToInternal.xslt transforms were updated to allow an incoming product representation not to include the Id attribute. If it is missing, an empty GUID is inserted in its place.

In the ProductController, the ItemPut method was updated to handle a missing Id in the representation. If it is missing, the Id from the URI is inserted.

The second change was the introduction of a ListPost method to handle representations being posted to the Products URI:

[AcceptVerbsAndOverloads(HttpVerbs.Post)]

[ActionName("List")]

public ActionResult ListPost(string categoryString)

{

    ActionResult actionResult;

    Product product = DeserializeIncomingRepresentation<Product>("Item", out actionResult);

 

    // If a category is supplied, make sure it is the same as in the representation

    Category category = ExtractCategoryAndVerify(categoryString, ref product,

                                                 ref actionResult);

 

    // The id, if supplied, must be an empty GUID. As this is a POST, the server decides

    // on the resource’s id

    if ((actionResult == null) && (product != null))

    {

        if (product.Id != Guid.Empty)

        {

            actionResult = SelectActionResult("ErrorDetail", new ErrorDetail()

            { Description = string.Format("The product id ({0}) cannot be embedded

              in the representation as the id of the resource is generated", product.Id)

            }, HttpStatusCode.BadRequest);

 

            product = null;

        }

    }

 

    // Give the new product an id and save away the new product

    if ((actionResult == null) && (product != null))

    {

        product.Id = Guid.NewGuid();

        Products.Save(product);

    }

 

    // Return the representation of the product and a status code of 201 Created

    // as we have created a new resource

    if ((actionResult == null) && (product != null))

    {

        actionResult = SelectActionResult("ItemGet", product, HttpStatusCode.Created);

        AppendLocationHeader(category, product);

    }

 

    return actionResult;

}

The code above is slightly different than ItemPut. It ensures no Id is provided in the representation and always creates a new Id for the resource.

The HTTP Response Code is set to 201 Created as the POST will always result in a new resource. Incidentally I also updated the ItemPut to return 201 Created if the representation it receives is for a new resource (so the first PUT results in 201 Created and subsequent PUTs to the same resource result in 200 OK).

Because the server effectively decides what the URI will be for the new resource, the response must pass the new URI back to the client. Therefore, the function above appends an extra header to the response, Location. The value for the Location header is the full URI of the new resource. To get the full URI requires a little bit of work with the UriBuilder class.

The code for this version can be downloaded: RESTfulMVCWebService10.zip (76.42 kb)

In my next post I will probably just tidy things up before moving onto embedding links to resources within DPoS.


Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading