Let's create a sample ASP.NET Web API application.

Just to mention, the application we are creating here is with very limited functionality. The purpose of this application is to go through the process of creating and setting up a web API application. Let's hit the Visual Studio(I will be using 2012 and C#)...

#1. Start Visual Studio and select new project. Go to under Web and select the template: ASP.NET MVC 4 Web Application. Let's give a name to the project, i will go with "SampleWebApi" and hit Ok.

ASP.NET Web API New Project

#2. It will ask to select a project template. Go with Web API.

ASP.NET Web API Project

#3. Expand the solution explorer. Right-click the Controllers folder and select Add > Controller. It will open a window to select a template. Name the controller and go with "Empty API controller". I named my controller as "TeamsController". Hit the Add Button. It will add a TeamsController.cs file and will open the file there. If we will notice, this class inherits from ApiController. ApiController class is part of ASP.NET Web API framework.
FYI: We could also have pressed Ctrl+M,C to open add new controller window.

ASP.NET Web API Project

#4. Let's add a model now. A model is basically class, which contains related fields/ values as its properties and used in transferring data. Right-click the Models folder and select Add > Class We will use this model in our controller class. Add some properties to this Team class:

public class Team
{
public int Id { get; set; }
public string Title { get; set; }
public string Owner { get; set; }
}
        

#5. Now as we will not be using any database in this application, but we need to have some team details, which can be exposed through our sample Web API application. So, let's create some hard-coded team details and add that in a list. Add this list to TeamsController class, which can be used as our teams resource.

private static  IList<Team> _teams = new List<Team>
{
new Team
{
Id = 100,
Title = "Team 100",
Owner = "Owner 100"
},
new Team
{
Id = 101,
Title = "Team 101",
Owner = "Owner 101"
},

// Add more teams
};
        

#6. Now let's add some basic action methods, which TeamsController will be exposing.

// GET api/teams
public IEnumerable<Team> Get()
{
return _teams;
}

// GET api/teams/100
public Team Get(int id)
{
return _teams.First(t => t.Id == id);
}

// POST api/teams
public void Post(Team team)
{
var maxId = _teams.Max(t => t.Id);
team.Id = maxId + 1;
_teams.Add(team);
}

// PUT api/teams/100
public void Put(int id, Team team)
{
var index = _teams.ToList().FindIndex(t => t.Id == id);
if (index >= 0)
{
_teams[index] = team;
}
}

// DELETE api/teams/100
public void Delete(int id)
{
var index = _teams.ToList().FindIndex(t => t.Id == id);
if (index >= 0)
{
_teams.RemoveAt(index);
}
}

#7. We are all set now. Let's press F5 and run the application. It will open default browser of Visual Studio.

#8. In browser address bar URI will be similar to this:
http://localhost:49215/
Port number may be different in your case.

Now let's get the list of teams by calling below action method.
http://localhost:49215/api/teams
Note: In Firefox/ Chrome we will see result in xml/ json format. In IE, it may ask to open or save .json file. Hit open in note pad, we will get result data in json format.

To get a particular team details use below URI:
http://localhost:49215/api/teams/100
We will see a particular team detail, with id 100.

Till now, we have checked GET action of sample Web API.

Let's override the convention
The application, we created above, is according to the convention of ASP.Net Web API default behavior. The default convention is to name the action methods in controller same as the HTTP method or add HTTP method name as prefix to name of action methods. Example: I used Get in above application to handle HTTP GET. We could also have chosen to name the action methods as GetTeam or GetAllTeams or GetTeamById.
Similarly, the action methods of Post, Put, and Delete will respectively handle HTTP POST, PUT, and DELETE.

So above are the default conventions to name action methods. We can choose any name for action methods; Its not necessary to go with that convention. But then how a particular HTTP action will be recognized, when we will call our web API? To help us here we have AcceptVerbs attribute or equivalent shorthand attribute HttpGet, HttpPost, HttpPut, HttpDelete.

Let's change TeamsController, we created above, customize and override the convention.

#1. Comment out all the action methods of TeamsController.cs. But keep the static list _teams.

#2. Let's add below action method in TeamController class.

[HttpGet]
public Team RetrieveTeamById(int id)
{
return _teams.First(t => t.Id == id);
}

Notice, that above action method does not follow the default convention. Also, i have used attribute HttpGet, instead we could also have used AcceptVerbs("GET").

#3. Now build the application and press F5. In browser address bar call this action method with URI: http://localhost:55778/api/teams/100
We will notice same response, as we have received in previous application. The only difference now is the action method that handles the GET request; it is now the RetrieveTeamById and not the Get method. But, attribute decorated above that tells that its a GET method.

#4. Below is an exmple of adding a PUT method by overriding the default conventions.

[HttpPut]
public void UpdateTeam(int id, Team team)
{
var index = _teams.ToList().FindIndex(t => t.Id == id);
if (index >= 0)
{
_teams[index] = team;
}
}

#5. Comment out above 2 action methods we added and add below method in TeamsController class.

[HttpGet]
public Team RetrieveTeamByIdRpcMode(int id)
{
return _teams.First(t => t.Id == id);
}

#6. Now to make RPC style work, we will need to do changes in WebApiConfig.cs file under App_Start folder. Let's add below code, above existing MapHttpRoute:

config.Routes.MapHttpRoute(
name: "RpcApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional}
);

#7. Now build the solution and press F5; Browser will open up.


Call the action method we created in #5 with this URI: http://localhost:49215/api/teams/RetrieveTeamByIdRpcMode/100
Again we will see same response as we saw before.

#8. Ok. So, we saw how RPC style works. Now let's remove the MapHttpRoute we added in #6.

Now if we notice, in all the above URI to make a web service call, we had api word in the middle. Like:
http://localhost:55778/api/teams/100

From where this "api" came from in URI?
Let's open the WebApiConfig.cs file once again.
We see this code block:

config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

In above code block as we see in route template we have api/{controller}/{id}. Requests get routed to a particular controller of web API with help of this mapping route. Let's match this with a URI:
http://localhost:55778/api/teams/100
http://localhost:55778/api/controller/id
So, its clear now, how it is getting routed to a particular action method of a controller.

#9. Let's modify the routeTemplate in MapHttpRoute

config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "customapi/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

Now build the solution; URI to access our web API is changed.
http://localhost:55778/customapi/teams/100
So, by using this MapHttpRoute we can customize our web API access URI as per our need/ requirement.

#10. Now let's revert the changes we did in #9 and modify the MapHttpRoute like below:

config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{compid}/{controller}/{id}",
defaults: new {id = RouteParameter.Optional},
constraints: new {compid = @"\d+"}
);

Now If we run the application and use this URI:
http://localhost:49215/api/teams/100, we will get error, "The resource can not be found".

What happened? This was working before!
As we changed the routeTemplate in MapHttpRoute to include one integer value as company id, so now it expects to pass an integer after the word "api" in URI.

Let's change the URI now.
http://localhost:49215/api/100/teams/100
Now its working but as we don't have any action method which can take compid and id as an argument, so we are getting this error: The requested resource does not support http method 'GET'.
Let's add one action method with 2 arguments like below:

public Team Get(int compId, int id)
{
return _teams.First(t => t.Id == id);
}

Now build and run the application. We will see it's working as expected now.

#11. Now let's revert the WebApiConfig changes as default one. So, we can use it further in this article.


Rules of HTTP and Status codes
Now let's take one more step forward. Let's use HTTP status codes in response.

HTTP GET to retrieve resource(s)

#1. First let's clear our controller class. Comment out all the action methods in TeamsController. But, leave the _teams static field.

#2. Add below action method there:

public Team Get(int id)
{
var team = _teams.First(t => t.Id == id);

if (team == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return _teams.First(t => t.Id == id); 
} 

#3. Rebuild and run the application. Now make a HTTP GET request.
http://localhost:49215/api/teams/1000
Note that, we do not have any team with id 1000. So, 404 - Not Found is returned, and Internet Explorer shows the same with the message that "The webpage cannot be found".

Let's add one more action method to return list of teams, by matching team titles.

#4. Add below action method to the TeamsController:

public IEnumerable<Team> GetTeamsByTitle(string title)
{
var teams = _teams.Where(t => t.Title.Contains(title));

if (teams == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}

return teams;
}

#5. Let's rebuild the application and run it. Make Get call to above action method.
http://localhost:49215/api/teams?title=Team
This will search for all the teams whose title contains the phrase "Team"

Now let's pass multiple fields to filter.

#6. Comment out the action method GetTeamsByTitle, created above. Now add a new class file, TeamFilter.cs under Models folder.

public class TeamFilter
{
public string Title { get; set; }
public string Owner { get; set; }
}

#7. Add a new action method for this:

public IEnumerable Get([FromUri] TeamFilter teamFilter)
{
var teams = _teams.Where(t => t.Title.ToLower().Contains(teamFilter.Title.ToLower()) &&
t.Owner.ToLower().Contains(teamFilter.Owner.ToLower()));

if (teams == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}

return teams;
}

#8. Rebuild and run the application. Call above action method.
http://localhost:49215/api/teams?title=Team&owner=owner
FYI: We could also have passed 2 separate parameters to the action method instead of passing complex object. In ASP.NET Web API to bind the query parameters to the complex type Filter, we must use the FromUri attribute.


Create Resource with HTTP POST

#9. Add a new atcion method for HTTP POST:

public HttpResponseMessage Post(Team team)
{
var maxId = _teams.Max(t => t.Id);
team.Id = maxId + 1;
_teams.Add(team);

// Prepare the response to return
var response = Request.CreateResponse(HttpStatusCode.Created, team);
var uri = Url.Link("DefaultApi", new { id = team.Id });
response.Headers.Location = new Uri(uri);
return response;
}

But to do this HTTP POST call and view response we will need a tool like Fiddler.


Create Resource with HTTP PUT

#10. Add a new action method for HTTP PUT now:

public HttpResponseMessage Put(int id, Team team)
{
if (_teams.Any(t => t.Id == id))
{
_teams.Add(team);
}

// Prepare the response to return
var response = Request.CreateResponse(HttpStatusCode.Created, team);
var uri = Url.Link("DefaultApi", new { id = team.Id });
response.Headers.Location = new Uri(uri);
return response;
}

When we need to create a resource with an identifier provided by client, we use HTTP PUT.

Overwriting a resource with HTTP PUT

A resource can be overwritten by HTTP PUT method.
FYI: This operation is generally considered the same as updating the
resource, but there is a difference. To issue a PUT request, we must send the representation of the whole resource in the request. Partial updates are should not be allowed through HTTP PUT.

#11. Let's modify the atcion method created in #10. So, if passed team id already exists then update the existing one else add new one.

public HttpResponseMessage Put(int id, Team team)
{
var index = _teams.ToList().FindIndex(t => t.Id == id);
if (index >= 0)
{
// overwrite the existing resource
_teams[index] = team;

// Prepare the response to return
return Request.CreateResponse(HttpStatusCode.NoContent);
}
else
{
_teams.Add(team);

// Prepare the response to return
var response = Request.CreateResponse(HttpStatusCode.Created, team);
var uri = Url.Link("DefaultApi",
new { id = team.Id });
response.Headers.Location = new Uri(uri);
return response;
}
}


Updating a resource with HTTP POST
A resource can be updated by HTTP POST method. It supports partial update too.
FYI: This operation is generally considered the same as updating the
resource, but there is a difference. To issue a PUT request, we must send the representation of the whole resource in the request. Partial updates should not be allowed through HTTP PUT.

#12. Let's modify the atcion method created in #10. So, if passed team id already exists then update the existing one else add new one.

public HttpResponseMessage Post(int id, Team team)
{
var index = _teams.ToList().FindIndex(t => t.Id == id);
if (index >= 0)
{
// Update the existing resource
_teams[index] = team;
return Request.CreateResponse(HttpStatusCode.NoContent);
}
return Request.CreateResponse(HttpStatusCode.NotFound);
}
 

Deleting a resource with HTTP DELETE

#13. To delete a request we will need a HTTP DELETE action method. Let's add below action method in TeamsController.

public void Delete(int id)
{
var team = Get(id);
_teams.Remove(team);
}

Now we have covered all the standard HTTP verbs. But we could not test POST, PUT and DELETE. To test these verbs, we will need a tool like fiddler.

Stay tuned, if looks interesting.

Reference:
Book: Practical ASP.NET Web API [Apress]

Prev > ASP.NET WEB API Part 1 - Concept of Web API

Next > ASP.NET WEB API Part 3 - Web debugging with Fiddler and browser


Discussion 1 Comment
10 X 5
** To prevent abusing comments from publishing, posted comments will be reviewed and then published!
 Mritunjay Kumar