Monday, December 2, 2013

Basic Routing in ASP.NET Web API 2

In the previous post, we discussed how we can create basic ASP.NET Web API based service and how we can request GET and POST requests to the service. In this post we would start exploring the routing a little further. In this example we will be building on top of the previous post introducing the idea of HTTP routes in ASP.NET Web API. You might need to download the code from previous post before starting following this.

HTTP Routes & Web API Configuration
The route configuration for ASP.NET Web API requests are specified in WebApiConfig. The route for the controller is specified as api/{Controller}. This is used to build routing table for Web API controllers. Using api should avoid any collision with ASP.NET MVC routes. It is a recommended convention. Here {controller} and {id} are placeholder variables. If there are no resource template in routing table matching the client request, the client receives a 404 message.

From client's Web API request, [x = {Controller}] would be mapped to xController. So in order to invoke StudentsController, we can specify the route as api/students. Updating a route configuration here, we can establish a different request format e.g. we can update it as api/MyControllers/{Controller} to require access configuration in the browser as follows:


Since we are accessing the resource using web browser, it would be sending GET requests by default.

As we know that HTTP request methods are mapped to Web API controller actions. HTTP methods are also referred as VERBS. Accessing a Web API service as above with GET method would invoke parameterless Get...() method by default. Here we are returning all the students from the collection. The response displayed on the browser is in xml format. In the last post we discussed how we can request data in a different format (including json) from ASP.Net Web API service.

Parameterized GET using the Request URI
As you can see above, the controller also has a Get...() with a parameter of int type. This would be used when a resource is requested using api/{controller}/id. Here id is any integer value which is used to determine the StudentId from the collection for the requested student.

ASP.NET Web API also supports parameterized GET requests. In order to entertain such requests, we can introduce a number of Get...() methods in our APIController based on the expected requests. The default policy rule is that parameters with simple (DateTime, Decimal, Guid, String, and TimeSpan) & primitive types can be picked up from request URI. The parameters with complex types are picked up from request body (unlike MVC) after applying the required formatters as specified in Content-Type in HTTP message. In the following, we have introduced two new methods to StudentsController for supporting parameters for supporting requests with studentName parameter or studentId & studentName parameters.

The above methods can be used by Web API when a client sends a request as in the following image. You should notice that the order of parameters doesn't affect as long as the names of parameters are matching with the request.

The documentation seems to suggest that the API supports optional parameters for these actions. It must be remembered that the action selection is based on matching most number of parameters. This is useful if there are more than one method supporting the same HTTP methods (e.g. GET), the method invoked would be dependent on the matching of most method parameters from the request URI.

Action Names from Request URIs
As we have been discussing that the ASP.NET Web API actions are picked up from HTTP requests. We can also define resource templates in such a way that the actions are picked up from URIs as in ASP.NET MVC. Let us update the route configuration in WebApiConfig as follows:

Since actions are not being picked up from HTTP verbs anymore, a request URI without action info would result in a 404 message as follows:

As we discussed in the previous post, we can use fiddler to compose HTTP requests. Here we are composing a HTTP POST message. Just notice that we have POST action as part of the request URI.

This flexibility allows us to incorporate any arbitrary action in our Web API. Since URI contains action name, it is easier to use the specified method from a client. Make sure you decorate the action with the accepted HTTP method details. Here we are supporting GET for the specified action.

Here we are requesting the above action from Fiddler. As you can see we have specified info as action

The action name can be overridden by decorating the action method with ActionName attribute. Here Info is decorated with [ActionName("GetInfo")]. This action would be used for HTTP verbs including GET and HEAD. It must be remembered that since method names are used for external action requests, so, if we don't want to expose a method for external Web API requests then we must be decorating it with [NonAction] attribute as we have done for GetInfoForId method.

Request URI and Place Holders
As we have discussed above, we can define route templates with placeholders. Their values are picked up from client request. In the following example {controller}, {action} and {id} are placeholder variables.

We can specify further details of these placeholders in the route template for ASP.NET Web API service. We have already seen how we can mark these place holders as optional (e.g. id). Further details include specifying default values in case the request is missing them. It also supports constraints for these placeholders to restrict the values in the request. It seems that the default values can only be at the end of request URI. So in the above route template, we can define a default value for {id} place holder but we cannot define a default value for {controller} and {action} placeholders.

In the example below, we are defining a resource templates with default values and constraints for {action} placeholder. The request is restricted to have values including get, post, put and delete for this placeholder. If the request contains any other action, the client just get a 404 HTTP error message.

We are also specifying get as the default value of this placeholder if request is missing it.


No comments: