Announcement: WCF Web API is now ASP.NET Web API! ASP.NET Web API released with ASP.NET MVC 4 Beta. The WCF Web API and WCF support for jQuery content on this site wll removed by the end of 2012.

Getting Started: Adding support for getting a single resource and supporting POST, PUT and DELETE methods

Learning objectives

In this quick start you will learn how to create a Web API that supports updates

  • How to enable retrieving a specific item resource
  • How to handle overriding the HTTP Response for exception cases
  • How to enable HTTP POST, PUT and DELETE methods on an API

Pre-requisites

  • Visual Studio 2010 / Visual Studio 2010 SP1
  • ASP.NET MVC 3*

*Note: Web API does not requires ASP.NET MVC, it supports supports multiple hosting configurations including self-host, IIS and ASP.NET (both MVC and Web Forms). This quick start however show show to host in ASP.NET MVC 3.

Scenario

For this quick start the scenario will be allowing client to add, remove and update contacts in the system

1 – Extract the starter code

To begin the quick start first extract the starter code / end code available here. Once the starter code is extracted, open the solution in the “Start” folder*

Note: The code is not identical to what was written in Quick start 1. The primary difference is that the entity code has been refactored into an in memory repository (ContactManagerRepository) which the API now uses.

2 – Enabling retrieval of a single resource and working with HttpResponseException

Our current API only supports retrieving a collection of contacts. Another common scenario is to return a single contact where the URI points to a sub-resource of the collection. If the contact is not available the API should return a status 404.

  • Open ContactsApi.cs
  • Add the method below to the ContactsApi class
[WebGet(UriTemplate = "{id}")]
public Contact GetItem(int id)
{
    var contact = repository.Find(id);
    if (contact == null)
        throw new HttpResponseException(HttpStatusCode.NotFound);
    return contact;
}
  • Notice the GET method accepts an ID parameter that maps to the {id} URI template parameter. Thus if the request URI is “http://localhost:9000/contacts/1” ID will be set to 1. Web API supports automatically casting URI template parameters to primitive types thus the int parameter is correctly handled for id.
  • If the contact is not present, then the an HttpResponseException is thrown setting the status code. We will test this later in the section on DELETE. HttpResponseException allows setting more than a status code, you can modify any part of the response.
  • Build and run the solution (F5)
  • Browse to the Web API test client by appending “/test” to the address
  • Click on {1} in the Resources view to populate the Request fields
  • Substitute 1 for {id} in the request URI
  • Change the Accept header value to “application/json”
  • Click the Send button
  • Contact 1 is returned in JSON.

image

  • Now try requesting Contact 10 – the Web API returns a 404/Not Found error code as expected

image

3 – Adding support for POST

In the snippet below, a new Post method is added which adds a new contact.

  • Switch to ContactsApi.cs
  • Copy the Post method below.
[WebInvoke(UriTemplate = "", Method = "POST")]
public Contact Post(Contact contact)
{
    repository.Add(contact);
    return contact;
}

Notice above the usage of the WebInvokeAttribute. For all other methods other than HTTP GET, this attribute is used. The Method parameter specifies the HTTP method and MUST be uppercased. The default HTTP method for the WebInvokeAttribute is POST, but here we choose to be explicit.

4 – Posting JSON

Web API allows content to be posted in multiple formats. Below you will use the Web API test client to POST in JSON.

  • Build and run the solution (F5)
  • Browse to the Web API test client by appending “/test” to the address
  • Click on the http://localhost:9000/api/contacts resource to populate the Request fields
  • Change the HTTP method for the Request to be POST

image

Notice that the support HTTP methods for the selected Resource are highlighted

  • Update the value of the Accept header to be “application/json”
  • Select the JSON tab in the Request Body editor and enter the content below into the Request Body
{ "Name": "New Person1" }

Notice that the Web API test client provides IntelliSense for expected member names as you type

image

Also, the value of the Content-Type header was automatically populated for you

image

  • Press the Send button
  • The result should be JSON and show that a new contact was created with an id of 7.

image

5 - Posting XML

  • To POST in XML, change the value of the Accept header to be “application/xml” (Note the Accept header is not technically needed in this case as Web API defaults to XML)
  • Switch to the XML tab in the Request Body editor to have your previous JSON request content automatically converted to XML
image
  • Change the Name of the contact to be “New Person2” (you can leave the ContactId as it will be ignored)
  • Press the Send button to POST the new contact
  • The result should be XML and show that a new contact was created with an ID of 8.

image

6 - Adding support for PUT

Adding support for PUT and DELETE is easy. Like POST you use the WebInvoke attribute specifying PUT and DELETE respectively the corresponding class methods.

  • Open ContactApi.cs and paste the following code.
[WebInvoke(UriTemplate = "{id}", Method = "PUT")]
public Contact Put(Contact contact, int id)
{
    var existingContact = repository.Find(id);
    if (existingContact == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    existingContact.Name = contact.Name;
    return existingContact;
}

Similar to GET, the PUT method contains logic to return a 404 status code If the contact is not found.

  • Build and run the solution (F5)
  • Browse to the Web API test client by appending “/test” to the address
  • Click {id} in the Resources view and set the method to PUT
  • Substitute 1 for {id} in the Request URI
  • Select the JSON tab in and add the following content to the Request Body
{ "Name": "Updated Contact" }
  • Click the Send button
  • The updated contact should get returned indicated that the PUT succeeded.
image
  • Change the Request HTTP method to GET
  • Change the value of the Accept header to be “application/json”
  • Click the Send button
  • The updated contact is returned.

image

7 -  Adding support for DELETE

  • Switch to ContactApi.cs and copy in the following code for the delete method.
[WebInvoke(UriTemplate = "{id}", Method = "DELETE")]
public Contact Delete(int id)
{
    var contact = repository.Find(id);
    if (contact == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    repository.Remove(id);
    return contact;
}

Similar to the GET and PUT methods the delete method also will return a 404 if it does not exist.

  • Build and run the solution (F5)
  • Browse to the Web API test client by appending “/test” to the address
  • Click {id} in the Resources view and set the method to DELETE
  • Substitute 1 for {id} in the Request URI
  • Click the Send button
  • The response indicates that the resource which has been deleted.

image

  • Change the method to GET and execute. A status 404 is returned as the resource is not found.

image

  • Chang method back to DELETE and execute.
  • A 404 is returned.

image

8 – Summary

In this quick start we learned the following

  • How to add support for retrieving a single resource
  • How to set a method to return a 404 / how to handle overriding the HTTP Response for exception cases.
  • How to support POST, PUT and DELETE.

You can find the completed code in the END folder where you extracted the starter code.

Last edited Feb 17, 2012 at 7:33 PM by danroth27, version 9

Comments

jawahar Feb 2, 2012 at 3:33 AM 
dennis
I am trying to use the example you mentioned, what namespace are used for HttpHostConfiguration and MediaTypeFormatterImage

jawahar

cmichaelgraham Nov 18, 2011 at 10:54 AM 
LOVE this sample.

Looking at "4 - Posting JSON":

* Since the Web API test client supports IntelliSense, could you elaborate on how my client can get the expected member names?

* Is the member type available also?

kelinw Oct 24, 2011 at 6:53 PM 
finally get chance to look into this, and made modification onto my WCF API for WP7 app, The method of GET, PUT, POST and DELETE worked perfectly in my case, simply this is the best practice for WCF API hosted on MVC project, also like the test tool alot, indeed, it more natual for my project instead of using fiddler. thanks much!

DennisvandeLaar Aug 16, 2011 at 8:50 AM 
Hi Nick,

I also build it with Unity and provided some details below maybe it can help you out:

The ContactsApi looks like this:

[ServiceContract]
public class ContactsApi
{
IContactRepository repository;

public ContactsApi(IContactRepository repo)
{
this.repository = repo;
}


[WebGet(UriTemplate = "")]
public IQueryable<Contact> Get()
{
return this.repository.Get();
}

[WebGet(UriTemplate = "{id}")]
public Contact GetContact(int id)
{
return this.repository.Get(id);
}

[WebInvoke(UriTemplate = "", Method="POST")]
public void AddContact(Contact contact)
{
this.repository.AddContact(contact);
}
}

My global.asax at the Application_Start like this:

IUnityContainer container = new UnityContainer()
.RegisterType<IContactRepository, ContactRepository>();

var config = HttpHostConfiguration.Create().SetResourceFactory((t,i,r) => container.Resolve(t), null)
.AddFormatters(new MediaTypeFormatterImage());

RouteTable.Routes.MapServiceRoute<ContactsApi>("contacts", config);

And my UnitTest:

ContactsApi contactApi = new ContactsApi(new FakeContactRepository());

Assert.AreEqual(3, contactApi.Get().Count<Contact>(), "Number of contacts is incorrect");
contactApi.AddContact(new Contact { ContactId=4, Name="abc"});

Assert.AreEqual(4, contactApi.Get().Count<Contact>(), "Number of contacts is incorrect");

It's working for me in both ways.

Regards,

Dennis

GoldenHornet Aug 11, 2011 at 3:46 PM 
Awesome sample and thanks to Dennis for the corrections (I had the same issues)

I have dropped Unity and Unity.Mvc Nupac packages into the project to increase the testability of the service. This works for the Mvc part of the project but when the ContactsApi class is called a System.InvalidOperationException is thrown (The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor)

I don't suppose anyone can point me in the direction of a sample for making this work with Unity? How do I get Unity to inject the repository in the ContactsApi constructor?

Cheers,
Nick

DennisvandeLaar Jul 13, 2011 at 9:08 AM 
The sample project, didn't compile on my machine at the start. I had to move the InMemoryRepository.cs and the IRepository.cs from the Infrastructure folder to the Repositories folder and reload the project.

2- enable retrieving
In the global.asax.cs file the following line is the reason that the first sample returns a 404
routes.MapServiceRoute<ContactsApi>("api/contacts");
In the first sample you have to retrieve the contact on http://localhost:9000/contacts/1 which doesn't work, change the url to http://localhost:9000/api/contacts/1

4-Posting Json
[WebInvoke(UriTemplate = " ", Method="POST")] --> change it to [WebInvoke(UriTemplate = "", Method="POST")]. In the first one is a whitespace in the UriTemplate. remove the whitespace and it works.

7- adding support for PUT
Also for the put example change the url: http://localhost:9000/contacts/7 to http://localhost:9000/api/contacts/7. Because your web app is rebuild the contact 7 isn't available anymore. Change the number to a number below 7.

Besides some minor fixes everything works good. Thanks for the quick start!!

senfo Jul 12, 2011 at 8:56 PM 
Does this sample work for you, as is? I had to add "api" to the URL: http://localhost:9000/api/contacts/1

I notice you used the correct URL in your JSON sample, but sample #2 is missing "api".