Friday, November 19, 2021

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.

No comments:

Post a Comment

No String Argument Constructor/Factory Method to Deserialize From String Value

  In this short article, we will cover in-depth the   JsonMappingException: no String-argument constructor/factory method to deserialize fro...