Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does ASP.NET Web.api handle two methods with names starting with GET?

I am looking at the following tutorial from Microsoft. As per this tutorial,

In the first example, "products" matches the controller named ProductsController. The request is a GET request, so the framework looks for a method on ProductsController whose name starts with "Get...". Furthermore, the URI does not contain the optional {id} segment, so the framework looks for a method with no parameters. The ProductsController::GetAllProducts method meets all of these requirements.

What happens if there are two methods like GetAllProducts() and GetSoldProducts()? Both have no parameters.

Your First Web API Tutorial

like image 426
WinFXGuy Avatar asked May 18 '12 19:05

WinFXGuy


People also ask

Can we have multiple get methods in Web API?

As mentioned, Web API controller can include multiple Get methods with different parameters and types. Let's add following action methods in StudentController to demonstrate how Web API handles multiple HTTP GET requests.

Which of the following method names are allowed in Web API for get?

HTTP verb GET mapped to a GET () or GetEmployees () method. So, in our EmployeeController we have GET () method as, So, by convention, get () is mapped to the HTTP verb GET. Even if we rename it to GetEmployee(), it will still be mapped to HTTP verb GET as long as method name is prefix with the word Get.

Can we have multiple get methods in controller?

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.

What is the default response type of a GET method in Web API?

In short, JSON format is the default content negotiator in web api 2. Also, User 3 requires data in text/html format but receives data in XML format. In short, text/html Accept header sends response in XML format by default.


3 Answers

There are two possible solutions to this specific problem:

  1. Alter MapHttpRoute calls to require specifying the name of the action. (I'm using Self-hosting syntax):

        config.Routes.MapHttpRoute(
                "API Route 1",
                "api/{controller}/{action}");
    
        config.Routes.MapHttpRoute(
                "API Route 2",
                "api/{action}",
                new { controller = "products" });
    

    So your http client would call:

    api/products/GetAllProducts OR api/GetAllProducts api/products/GetSoldProducts OR api/GetSoldProducts

    See: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

  2. Place each method in a separate controller (ProductsController, SoldProductsController). So you would call api/products and api/soldproducts to get your results.


Related topic... in the situation where you have a multiple Get actions that have a single primitive argument of the same type, ASP.NET Web API will look at the name of the argument to resolve which overloaded action to call.

For example, if you have two actions:

GetProductByName(string name) 
GetProductByCategory(string category) 

your http client can call

api/products?name=hammer 
api/products?category=toys

and the routing engine will call the correct action.

like image 186
Lee Grissom Avatar answered Oct 11 '22 13:10

Lee Grissom


Assuming you're using the default routes the short answer is : the method defined first (at the top) of your class will be called. the other method is inaccessible.

NOTE : the beta behaved as above for 'matching multiple methods' - the RC & Release version is a bit more OCD. It throws an error if there are multiple potential matches. This change removes the confusion of multiple ambiguous matches. At the same time, it reduces our ability to mix REST and RPC style interfaces in the same controller, relying on the order & overlapping routes.

Stealing liberally from another post I wrote on the topic:

WebAPI Matching Semantic

The matching semantic used by WebAPI is fairly simple.

  1. It matches the name of the action with the verb (verb = get? look for method starting with "get")
  2. if a parameter is passed, the api seeks an action with a parameter

So in your code sample a GET request without a parameter matches the Get*( ) function without an parameters. A Get containing and ID looks for a Get***(int id).

Examples

While the matching semantic is simple, it creates some confusion for MVC developers (well at least this developer). Lets look at some examples :

Odd Names - Your get method can be named anything, so long as it starts with "get". So in the case of a widget controller you can name your functions GetStrawberry() and it will still be matched. Think of the matching as something like : methodname.StartsWith("Get")

Multiple Matching Methods - What happens if you have two Get methods with no parameters? GetStrawberry() and GetOrange(). As best I can tell, the function defined first (top of the file) in your code wins ...strange. This has the side effect of making some methods in your controller unreachable (at least with the default routes)....stranger.

UPDATE

@WinFXGuy - This was a bit long to put in a comment, but ...

Don't jump to conclusions! I tried to answer the question you posed, but that's only half the story. There is plenty you can do to change the default behavior.

First, WebAPI supports much of the oData spec. If you bubble an IQueryable up to your controller, oData paramaters are automatically integrated with the query object. It takes parameters like $filter, $top, and $skip. So you in your case you can write one method and pass something like $filter=sale_date neq null.

Additionally, you can apply the [ResultLimit] attribute to prevent people asking for 15 billion records.

Second you can modify the routes. The default routes aim towards a RESTful api, where you generally have 1 controller per entity. You can change the routes and make it RPC style.

If you look at my linked post I explain how I kept the default route binding, added 'sub folders' and also allowed additional method calls for scenarios where i needed GetAllProducts() and GetSoldProducts().

like image 39
EBarr Avatar answered Oct 11 '22 12:10

EBarr


Adding an answer to reflect that the latest version of Web API supports [Route] attribute natively

[Route("api/products")]
public IEnumerable<Product> GetAllProducts(){}

[Route("api/products/sold")]
public IEnumerable<Product> GetSoldProducts(){}
like image 27
hunter Avatar answered Oct 11 '22 13:10

hunter