Multiple GET and POST methods in ASP.NET Core Web API
In ASP.NET Core MVC and Web API are parts of the same unified framework. That
is why an MVC controller and a Web API controller both inherit from Controller
base class. Usually a Web API controller has maximum of five actions - Get(),
Get(id), Post(), Put(), and Delete(). However, if required you can have
additional actions in the Web API controller. This article shows how.
Let's say you have a Web API controller named CustomerController with the
following skeleton code.
[Route("api/[controller]")]
public class CustomerController : Controller
{
[HttpGet]
public IActionResult Get()
{
}
[HttpGet("{id}")]
public IActionResult Get(string id)
{
}
[HttpPost]
public IActionResult Post([FromBody]Customer obj)
{
}
[HttpPut("{id}")]
public IActionResult Put(string id, [FromBody] Customer obj)
{
}
[HttpDelete("{id}")]
public IActionResult Delete(string id)
{
}
}
Now suppose that you wish to add another GET action that returns data based
on a given city and country. How can you accomplish this task?
In this specific case your new Get() action will have two parameters - city
and country. This doesn't violate any overloading rules and hence you can write
the following variation of Get() to get the job done.
[HttpGet("{city}/{country}")]
public IActionResult Get(string city, string country)
{
}
Notice that the [HttpGet] attribute now has two route parameters named city
and country. The underlying Get() action has the corresponding method
parameters. With this Get() action in place you can invoke it using the
following URL :
As you can see customers from UK and London city are being returned from the
Web API.
So far so good. But what if you want to have another Get() variation that has
same signature to an existing Get(). In this case overloading won't work since
the signatures will conflict with each other. Luckily, you can resort to
attribute routing to get the job done.
Suppose you want another Get() variation that returns data based on a
specific country. So the signature is going to look like this :
public IActionResult Get(string country)
{
}
This will conflict with :
public IActionResult Get(string id)
{
}
To tackle the problem you need to define a route as shown below :
[Route("[action]/{country}")]
[HttpGet]
public IActionResult GetByCountry(string country)
{
}
Notice that the [Route] attribute now includes [action] token and {country}
route parameter. The action name is GetByCountry(). To invoke this action you
need to explicitly specify the action name in the URL as shown below :
Now let's see how to deal with multiple actions for POST verb.
Suppose that you wish to have an additional POST action that takes a
parameter of some different type. Have a look below for an example :
[HttpPost]
public IActionResult Post([FromBody]Customer obj)
{
}
[HttpPost]
public IActionResult Post([FromBody]CustomerOrder obj)
{
}
These actions will compile successfully but will fail at runtime. That's
because POST mapping will be ambiguous and the framework won't be able to decide
which of the two actions is to be used.
You can again resort to routes to rectify the situation :
[HttpPost]
[Route("[action]")]
public IActionResult PostCustomerAndOrder
([FromBody]CustomerOrder obj)
{
}
Here the route includes the action name - PostCustomerAndOrder.
How will you invoke this action? A fragment of jQuery code follows :
var options= {};
options.url = "/api/customer/PostCustomerAndOrder";
options.type = "POST";
options.contentType = "application/json";
options.data = JSON.stringify(obj);
options.dataType = "json";
options.success = function (msg) {
console.log(msg);
};
options.error = function (msg) {
console.log(msg);
};
$.ajax(options);
The URL includes the action name and the verb used is POST. If you wish to
invoke the first Post() then the URL would be :
...
options.url = "/api/customer";
options.type = "POST";
...
Just like GET and POST verb you can deal with multiple actions for PUT and
DELETE verbs.
That's it for now ! Keep coding !!