Using HTTP Methods
(GET, POST, PUT, DELETE AND HEAD) in Web API
What
Are HTTP Methods?
Whenever a client
submits a request to a server, part of that request is an HTTP method, which is
what the client would like the server to do with the specified resource. HTTP
methods represent
those requested actions. For example, some commonly-used HTTP
methods will retrieve data from a server, submit data to a server for
processing, delete an item from the server's data store, etc. For a more
general overview of HTTP, see Tutorials
Point's article.
Selecting
the Appropriate Method
A large portion of
application functionality can be summed up in the acronym, which stands for
Create, Read, Update, and Delete. There are four HTTP methods that correspond
to these actions, one for each, like so:
C - Create - POST
R - Read - GET
U - Update - PUT
D - Delete – DELETE
R - Read - GET
U - Update - PUT
D - Delete – DELETE
H - Head – HEAD
So, in a given app,
you might have the following action:
public IHttpActionResult Add(string title)
{
//Creates a Movie based on the Title
return Ok();
}
We can tell from
the name of the action (and, let's be real, the comment) that this action is
supposed to create a movie. So we should use the POST verb on this action, like
so:
[HttpPost]
public IHttpActionResult Add(string title)
{
//Creates a Movie based on the Title
return Ok();
}
If you need a
particular action to support more than one HTTP method, you can use the [AcceptVerbs] attribute:
[AcceptVerbs("POST", "PUT")]
public IHttpActionResult Add(string title)
{
//Creates a Movie based on the Title
return Ok();
}
For the majority of
applications, GET, POST, PUT, and DELETE should be all the HTTP methods you
need to use. However, there are a few other methods we could utilize if the
need arises.
·
HEAD: This is identical to a GET request, but
only returns the headers for the response, not the response body. Theoretically
faster, commonly used for checking to see if a particular resources exists or
can be accessed.
·
OPTIONS: Returns the HTTP methods supported by
the server for the specified URL.
·
PATCH: Submits a partial modification to a
resource. If you only need to update one field for the resource, you may want
to use the PATCH method.
POST
vs PUT
POST and PUT are
very similar in that they both send data to the server that the server will
need to store somewhere. Technically speaking, you could use either for the
Create or Update scenarios, and in fact this is rather common. The difference lies
in the details.
PUT is idempotent.
What this means is that if you make the same request twice using PUT,
with the same parameters both times, the second request will have no effect.
This is why PUT is generally used for the Update scenario; calling Update more
than once with the same parameters doesn't do anything more than the first call
did.
By contrast, POST
is not idempotent; making the same call using POST with same parameters each
time will cause two different things to happen, hence why POST is commonly used
for the Create scenario (submitting two identical items to a Create method
should create two entries in the data store).
(It should be noted
that, strictly speaking, HTTP does not force PUT to be idempotent, so you can
implement your server to use PUT in a non-idempotent way. However, doing so is
liable to cause a horde of angry server admins to show up at your desk and beat
you with ethernet cables. Don't say I didn't warn you.)
Default
HTTP Methods
If we do not assign
an explicit HTTP method to a controller action, what method(s) does that action
accept? Let's imagine we have a Web API controller like so:
public class MovieController : ApiController
{
/// <summary>
/// Returns all movies.
/// </summary>
/// <returns>A JSON list of all
movies.</returns>
[Route("movies/all")]
public IHttpActionResult All()
{
List<Movie> movies = new List<Movie>()
{
new Movie()
{
Id = 1,
Title = "Up",
ReleaseDate = new DateTime(2009,5,29),
RunningTimeMinutes = 96
},
new Movie()
{
Id = 2,
Title = "Toy Story",
ReleaseDate = new DateTime(1995, 11, 19),
RunningTimeMinutes = 81
},
new Movie()
{
Id = 3,
Title = "Big Hero 6",
ReleaseDate = new DateTime(2014, 11, 7),
RunningTimeMinutes = 102
}
};
return Ok(movies);
}
}
We can tell by
looking at the code that this should be a GET action, since it is returning
data. However, we're not explicitly saying that GET should be used (there's no [HttpGet] attribute).
So, what method(s) will this action accept? Let's see what Postman can tell us.
It should be a GET
action, so let's try to hit this action with a GET request.
Well, that didn't
work, we get back a 405 Method Not Allowed status. Why were we not able to use
the GET method?
The algorithm ASP.NET uses to calculate the
"default" method for a given action goes like this:
1. If
there is an attribute applied (via [HttpGet], [HttpPost], [HttpPut], [AcceptVerbs], etc), the action
will accept the specified HTTP method(s).
2. If
the name of the controller action starts the words "Get",
"Post", "Put", "Delete", "Patch",
"Options", or "Head", use the corresponding HTTP method.
3. Otherwise,
the action supports the POST method.
We're falling in to
the #3 condition here: the action name All() doesn't contain any of the key words and we
didn't specify an action, so this action will only support POST. Sure enough,
guess what Postman shows for a POST action?
Obviously, this is
not what we want. We're getting data from the server using a POST method, and
this (while not technologically prevented) is not what these HTTP methods were
designed for.
We could solve this
problem in two ways. The first would be to add the [HttpGet] attribute to
the method. The second would be to rename the method to GetAll(); the existence of
the word "Get" at the start of the method tells ASP.NET to accept a
GET HTTP method on this action. My personal preference is to always
explicitly state which HTTP method is accepted by any action, like so:
public class MovieController : ApiController
{
/// <summary>
/// Returns all movies.
/// </summary>
/// <returns>A JSON list of all
movies.</returns>
[Route("movies/all")]
[HttpGet]
//Always explicitly state the accepted HTTP method
public IHttpActionResult All()
{
//Get movies
return Ok(movies);
}
}
“The HEAD method is
identical to GET except that the server MUST NOT return a message-body in the response.
The metainformation contained in the HTTP headers in response to a HEAD request
SHOULD be identical to the information sent in response to a GET request. This
method can be used for obtaining metainformation about the entity implied by
the request without transferring the entity-body itself. This method is often
used for testing hypertext links for validity, accessibility, and recent
modification.”
So, basically an
HTTP HEAD operation is a GET that returns only headers, no message body.
My idea was to
implement a HEAD method in our API controller that took the same arguments as
our GET method and then return the version number as a header in the response.
Sounded easy enough. Since Web API’s default convention is to map controller
operations to HTTP verbs, I figured all I needed to do was add a method to my
controller called Head along with the necessary parameters. I gave it a try in
a test project and saw the following result when I tried the new Head operation
in Fiddler:
My controller
action looked right:
1 2 3 4 5 6 7
|
Since my first
attempt yielded a 404, I decided I’d try something I’d only used in a web
controller before, using the AcceptVerbsAttribute. My updated method looked
like so:
1 2 3 4 5 6 7 8
|
[AcceptVerbs("HEAD")]
public HttpResponseMessage Head(int id) { HttpResponseMessage message = new
HttpResponseMessage(HttpStatusCode.OK);
message.Headers.Add("InputId", id.ToString()); return message; }
|
With the attribute
in place, I tried Fiddler again and saw much better results:
Not only was I now
getting a 200 response, my custom header was even included.