Posted on 6/11/2020 2:06:03 PM by Admin

Web API Routing

Before understanding ,what is routing ,let me explain you why even the concept of routing has been introduced , and the simplest reason that comes in my mind is ,it enables you define URLs of your services and web pages.

Let's discuss some points regarding URLs.

How the URLs of a web application should look like.

See the below URLs of a blog site :-
  1. Home/Articles/3/4
  2. articles/csharp/array
  • obviously the first one is technical and not user friendly , also it is not informative, on the other hand the other one is user friendly , well structured and informative, anybody can tell only by seeing the URL about the content they are going to get with this request. Nowadays people like to share the link if they liked it over various social platforms and believe me a user friendly URL gets more exposures.
  • second , it boosts your SEO , it makes sense that if your page's URL contains keywords and your page content really describes those keywords , then chances of ranking well in the search engines becomes higher.

How the URLs of a REST service should be

Let me explain some concepts which you will find unrelated at a first glance but at the end of this article , you will be able to relate everything easily.

1. GetAllStudent is a verb ,as it is pointing to an action ,which does something. Students are noun ,as it is pointing towards a group of students. Now remember the Rest Principles
  • Everything on web is a resource.
  • Each resource has a URL.

Now ,use common sense and say which of the below URL belongs to a rest service.
  1. http://myapp/api/student/GetStudentCourse/2 or
  2.http://myapp/api/students/2/course

Obviously ,the second URl is better than the first one from rest point of view,because it is pointing to a resource on server while the first one is action based which is against Rest principle. So, the rule of thumb is if you are creating a restful service then your URL will have noun segments and not a verb.

2. Path parameters (routed data) are used to identify a resource on the server while query strings are used to sort or filter a resource. So, until you don't want filtering never use query string parameters in the URL , in other words a rest service will not have a URL with query string parameters unless it is filtering or sorting a resource.
for example:- http://api/students/1, is a good URL than http://api/students?Id=1, from Rest point of view , as the URL is pointing towards a particular student resource not filtering the resource based on Id. But http://api/students/2/courses?category=software , is a good URL even from the rest point , as it is actually filtering the courses taken by a particular student based on some category. obviously , anyone can debate on this , but this is what I think a URL should look like for a rest service .

so , now we know how the URLs of a rest service should look like , let's try to achieve the same , obviously the first step would be creating a Web API project and create any service as you wish , I am going to create a Student service where there will be two methods one which will expose students data and other will expose the list of courses taken by a particular student.

Step 1.Create two model classes , Student and Course.


   public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public List Courses { get; set; }
    }

   public class Course
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

Step 2.Create a Controller and Name it StudentService and Create 3 methods as shown below.


   public class StudentServiceController : ApiController
    {

        public List LoadStudentData()
        {
            List students = new List()
            {
                new Student(){Id=1,Name="Sachin",Courses=new List(){new Course(){Id=1,Name="CSharp"},new Course() 
    {Id=2,Name="Asp.Net"}}},
                 new Student(){Id=1,Name="Vikash",Courses=new List(){new Course(){Id=1,Name="CSharp"},new Course() 
     {Id=2,Name="Asp.Net"},new Course(){Id=3,Name="KendoUI"}}},
                  new Student(){Id=1,Name="Arjun",Courses=new List(){new Course(){Id=1,Name="CSharp"},new Course() 
     {Id=4,Name="Angular"}}},
            };
            return students;
        }
        public HttpResponseMessage GetAllStudent()
        {
            try
            {
                var students= LoadStudentData();
                if(students!=null){
                    return Request.CreateResponse(HttpStatusCode.OK, students);
                }
                return Request.CreateErrorResponse(HttpStatusCode.NotFound,"no student found");
            }
            catch
            {
                return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Error");
            }
        }
        public HttpResponseMessage GetStudentCourses(string studentName)
        {
            try
            {
                var student = LoadStudentData().Where(s=>s.Name.ToLower()==studentName.ToLower()).SingleOrDefault();
              
                if (student != null)
                {
                    var courses = student.Courses.ToList();
                    return Request.CreateResponse(HttpStatusCode.OK, courses);
                }
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, "no student found");
            }
            catch
            {
                return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Error");
            }
        }
     }

At this point our service is ready , now we want the following URLs for our service.
  1. All Student :- http://localhost:12345/api/Students
  2. Course taken by a student:- http://localhost:12345/api/students/{studentName}/courses

Now , the challenge that comes is , how to tell the Web API that if either of the above requests come then map it to appropriate handlers meaning if the request is http://localhost:12345/api/Students then invoke the GetAllStudent() action of StudentService controller and if the request is http://localhost:12345/api/students/{studentName}/courses , then invoke GetStudentCourses() action of StudentService controller.

And this is the place where exactly the Routing comes into the picture. Routing is a pattern matching system that is responsible for mapping incoming http requests to specific controller action. When the application launches then application registers one or more URL patterns with the framework's route table to tell the routing engine what to do with any requests that matches those patterns.

In routing concepts there are three phases to reach the destination. That means invoking exact action method. They are:
  • Finding the exact Route Template
  • Finding the Controller
  • Finding the Action Method

1. Finding the exact Route Template

Here you must be thinking ,what exactly is the route template? Route Template is simply a URI which consists of placeholders.

Example: "API/{controller}/{id}"

The above example looks like a URL but it is having placeholders called controller and id. These placeholders are kept within curly braces.

Note:
  • Route Template can also have literals, like "API/{controller}/{id}". Here API is the literal.
  • Placeholder's value can be given in the defaults of the Route. Like defaults: new { Controller="Employee" }.

Explanation

Route Template acts as an entry point of the Routing in ASP.NET WEB API. Because, if the given or requested URI is matched with the Route Template then only the API looks for the controller and action method.

When Request has come to the API, first of all it tries to match the URI with one of the Route Templates. If the template has literals, then API checks for exact match of characters. For the placeholders it does not verify the exact value. In the above example Route Template has 'API' literal. So that API checks the same literal in the Route Template.

Note:
  • API does not check URI's host name.
  • API selects the first route which is matched with route template.
  • If the placeholder's value is given in default of the Route, then the URI will not have the value for that placeholder.

Example: URI: http://localhost/API/1
This URI match with the above mentioned Route Template as Controller placeholder value has already given in the default of the Route.
Once the Requested URI is matched with Route Template, placeholder's values are stored as the KeyPair Values in Route Dictionary. Keys are the placeholders and these Values are taken from the URI.
Route Dictionary values based on the above Route Template are Controller:Employee and id:1.

2. Finding the Controller

Finding the controller is simple. The API or Framework will get the Controller name from the Controller KeyValue of the Route Dictionary. From the above example Controller value is Employee. This value is been taken as Controller key has Employee value. Now, the framework appends Controller string to the KeyValue and Search for that controller. i.e EmployeeController. If there is no match for this controller then API returns error message.

3. Finding the Action Method

This is a little typical phase of Routing. To find the exact action method, framework gets the three values. They are:
  • The Http Method of the Request.
  • Action placeholder value from the route dictionary.
  • Parameters of the actions.

Here Http Method plays an important role in selecting the exact action method. And, important aspect is that the Action methods are selected based on the Naming Convention of Http method or Attribute placed with Http Method above it.

Explanation:
  • If the requested action method is of type HttpGet, either Action name would be Get or it should start with Get___.
Example: GetEmployee() or GetEmployeeDetails() actions will be selected.
  • The Action method selection will be made on the attribute as well.


  [HttpGet]  
   public HttpResponseMessage ShowEmployee()  
    {  
           //To Do  
    }  

Note:
  • The action methods which are of type public and are inherited by the APIController class are selected. If the method is private or not inherited by the APIController, framework returns an error.
  • If the Action Method not satisfied with naming convention or Attribute is not placed above the method, by default the framework treats the action method as HttpPost method. (In case the Controller have specified action name which matches the exact action key value of Route Dictionary.)

Now you know what is routing and Why routing is necessary lets come to our requirement , our student service is ready and we want following URLs for our service
  1. All Student :- http://localhost:12345/api/Students
  2. Course taken by a student:= http://localhost:12345/api/students/{studentName}/courses

Now , what do you think , our first step should be? obviously , we have to define the Route templates according to the URL structure we want and add it to the framework's Route table. Now, the question that arises is , where to define the route template and how to add it to the RouteCollection (route table) , as we had RouteConfig class in MVC to define route templates, in Web API we have WebApiConfig class which has a static Register method which accepts HttpConfiguration instance , the HttpConfiguration class has a public property "Routes" of type HttpRouteCollection , which is extended to add various route templates, in short MapHttpRoute() is an extension method of RouteCollection (routes) where we define route templates like shown below.


   public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            // Web API routes
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute(
               name: "AllStudent",
               routeTemplate: "api/students",
               defaults: new { controller = "StudentService" }
           );
            config.Routes.MapHttpRoute(
              name: "StudentCourse",
              routeTemplate: "api/students/{StudentName}/courses",
              defaults: new { controller = "StudentService" }
          );

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }

Explanation:-
1. To get all students:- whenever a request comes that matches the URL template "api/students" the framework gets the controller's value from the default and save it to the route dictionary in key value pair , the SelectController method gets the controller value from the dictionary and appends Controller string to it and invoke that controller , as the http method is get , the framework tries to invoke the method which starts with get , in our case both the method starts with get but one is parameter less and other is parameterized , as the dictionary does not have any Key value pair for parameter binding stored, the parameter less method is invoked.
2. To get a student course :- whenever a request comes that matches the URL template "api/students/{StudentName}/courses" the framework gets the controller's value from the default and StudentName from the URL and save it to the route dictionary in key value pair ,notice here the dictionary also contains the StudentName, the SelectController method gets the controller value from the dictionary and appends Controller string to it and invoke that controller , as the http method is get , the framework tries to invoke the method which starts with get , in our case both the method starts with get but one is parameter less and other is parameterized , as the dictionary do have StudentName:something as Key value pair for parameter binding , the parameterized method is invoked.

Different ways of defining routes in Web API:-

Web API supports two types of routing:-
1.Convention based routing and
2.Attribute routing.

• Convention based routing is very much similar to MVC routing , here we define one or more route templates, which are basically parameterized strings, at one place that is in the WebApiConfig class. When the framework receives a request, it matches the URI against the route template , The only difference is that Web API uses the HTTP verb, not the URI path, to select the action. But we can also use MVC-style routing in Web API and can provide Action name in the route template , though from rest point of view it is not recommended. In the above example we have used convention based routing.
• convention-based routing makes it hard to support certain URI patterns that are common in RESTful APIs. For example, resources often contain child resources like Customers have orders, movies have actors, books have authors,students have courses and so forth. It's natural to create URIs that reflect these relations: students/1/courses. This type of URI is difficult to create using convention-based routing. Although it can be done, the results don't scale well if you have many controllers or resource types. That is why Web API 2 supports a new type of routing, called attribute routing. As the name implies, attribute routing uses attributes to define routes. Attribute routing gives us more control over the URIs in our web API. For example, we can easily create URIs that describe hierarchies of resources like below example.


   [Route("api/students/{StudentName}/courses")]
        public HttpResponseMessage GetStudentCourses(string studentName)
        {
            try
            {
                var student = LoadStudentData().Where(s=>s.Name.ToLower()==studentName.ToLower()).SingleOrDefault();
              
                if (student != null)
                {
                    var courses = student.Courses.ToList();
                    return Request.CreateResponse(HttpStatusCode.OK, courses);
                }
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, "no student found");
            }
            catch
            {
                return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Error");
            }
        }

Note:- In order to use attribute routing with Web API, it must be enabled in WebApiConfig by calling config.MapHttpAttributeRoutes() method.

At last , if anything is defined it must be called from somewhere in the application , we know the routes should be stored in the route table before any requests hits the Web API , meaning when the application launches for the very first time the route templates should be added to the route table ,so the best place to call the Register method of WebApiConfig class is the application_Start event of global.asax like shown below.


       protected void Application_Start()
        {
            GlobalConfiguration.Configure(WebApiConfig.Register);
        }

Now , when you run the application you get the desired results when request is made through appropriate URLs.

Courses taken by a student
Courses taken by a student
All students list
Details of all students