Global And Per-Route Message Handlers In Web API

.NET has two main musketeers which are known as HttpModule and HttpHandler. Both have a vital role and work as per their need. Web API also uses HttpHandler to handle the HttpRequest. In very basic words Handler is “An agent who handles something”. HttpHandler works in the same way and takes care of Http requests which is also a hub ofthe Http universe. A simple example can be, “Client sends a request to Http server than server accepts this and takes anticipated processing and send back to client as HttpResponse."

In this article, we’ll see how Http Message handler works in Web API and actual flow of each Http request. Here is a diagram which shows the execution flow of each request and processing of each handler exists.


Note: This diagram is as per my understanding of Web API pipeline.

As you can see from the image shown above, Web API is hosted somewhere on IIS of self-hosted. An HttpRequest comes to HttpServer and converts into HttpReuqestMessage, which is further routed to Delegate Handler. Specifically there are two forms of message handler pipelines in Web API. First is normal handler pipeline in which all requests for all routes pass through and second one relates to specific route and called as per route. In general all requests come to normal handler pipeline and  areprocessed, whilst per route comes in picture only when there is a specific route (May be any Http request should not processes or that route should be ignored).There can be plenty of scenarios.

Here, I’ve a structure of the solution that has two handlers as in the following screenshot,


A MessageHandler is normal class file which inherits an abstract classDelegatingHandler. Two handlers exist in solution CustomHanlder_one and CustomHanlder_Two.

A code segment for two handlers named as CustomHandler_one and CustomHandler_Two are shown below:

  1. namespace WebApiDemo  
  2. {  
  3.     public class CustomHandler_One: DelegatingHandler  
  4.     {  
  5.       protected async overrideTask < HttpResponseMessage > SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)   
  6.         {  
  7.             // Call the inner handler.  
  8.             var response = awaitbase.SendAsync(request, cancellationToken);  
  9.             response.Headers.Add("X-Powered-By""DotnetPiper : Value returned from CustomHandler One.");  
  10.             Debug.WriteLine("Process response");  
  11.             return response;  
  12.         }  
  13.     }  
  14. }  
  15. CustomHandler_Two.cs code segment as given below:  
  16.     namespace WebApiDemo   
  17.     {  
  18.         publicclassCustomHandler_Two: DelegatingHandler  
  19.         {  
  20.             protectedoverrideTask < HttpResponseMessage > SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)   
  21.             {  
  22.                 // Create the response.  
  23.                 var response = newHttpResponseMessage(HttpStatusCode.OK)  
  24.                 {  
  25.                     Content = newStringContent("I'm called from Custom Delegate Hanlder Two !")  
  26.                 };  
  28.                 // Note: TaskCompletionSource creates a task that does not contain a delegate.  
  29.                 var tsc = newTaskCompletionSource < HttpResponseMessage > ();  
  30.                 tsc.SetResult(response); // Also sets the task state to "RanToCompletion"  
  31.                 return tsc.Task;  
  33.             }  
  34.         }  
  35.     }  
There are ways to register Global and per-route handler in WebApiConfig.cs file as given in the following screenshot. I’ve shared with you that global handlers work for all requests while Per-route handler is route specific. To register gl.obal and Per-route Delegate Handler please follow the below screenshot,


Let’s see execution order of global handler and how can we validate and control the response.

Here, I’ve a simple controller Employees which has the following Action Methods defined below and performs a set of statement like fetch a record from collection. GetDetails() action method takes one parameter and has its own specific routes while GetEmployeeByID() action method takes two parameters as arguments and has its own defined route.

  1. public Employee GetDetails(int id)  
  2. {  
  3.     return listEmp.First(e => e.ID == id);  
  4. }  
  1. [HttpGet]  
  2. [Route("api/{employees}/{id}/{userName}")]  
  3. [ActionName("GetEmployeeByID")]  
  4. public Employee Get(int id, string userName)   
  5. {  
  6.     return listEmp.First(e => e.ID == id);  
  7.     SqlDataReader reader = null;  
  8.     SqlConnection myConnection = newSqlConnection();  
  9.     myConnection.ConnectionString = @ "Server=.;Database=DBCompany;User ID=sa;Password=db@1234;";  
  11.     SqlCommand sqlCmd = newSqlCommand();  
  12.     sqlCmd.CommandType = CommandType.Text;  
  13.     sqlCmd.CommandText = "Select * from tblEmployee where EmployeeId=" + id + "";  
  14.     sqlCmd.Connection = myConnection;  
  15.     myConnection.Open();  
  16.     reader = sqlCmd.ExecuteReader();  
  17.     Employee emp = null;  
  18.     while (reader.Read())  
  19.     {  
  20.         emp = newEmployee();  
  21.         emp.EmployeeId = Convert.ToInt32(reader.GetValue(0));  
  22.         emp.Name = reader.GetValue(1).ToString();  
  23.         emp.ManagerId = Convert.ToInt32(reader.GetValue(2));  
  24.     }  
  25.     return emp;  
  26.     myConnection.Close();  
  27. }  
I run my application and verify whether API is up or not. Kindly find below image to identify this.


It shows API is running and ready to perform some action. Along with this I’ve Postman running aside where I paste the specific url (http://localhost:57888/api/employees/5/sachinkalia) which meets the requirement as shown below:


As soon as I click on the Send button it will go to CustomHandler_one first, which is global Handler and later goes to the respective controller as in the following screenshot. Kindly run an application once to understand practically.



  1. There is a code segment i.e. base.SendAsync (request,cancellationToken) in the above screenshot which takes all privilege to call inner handler (Call to respective controller’s action). As soon as you get response from inner handler you may customize that response also like I’ve added header (X-Powered-By) into the received response. So this is how you can customize the response as well using global handlers. Kindly find the screenshot as depicted below:


    Now let’s take per-route handler execution and how it skips the rest of the pipeline execution. I run my application again and verify the per-route execution.

    It shows API is running and ready to perform some action. Along with this, I have Postman running along side where I paste the specific url (http://localhost:57888/api2/employees/5) .


    As you have seen above how can we skip the rest of pipeline in Web API using Per-Route handler but sometimes you may also want that Web API pipeline should execute normally. To achieve that you may have to make few changes in WebApiConfig.cs file for Per-Route handler as shown below in screenshot.


    As soon as I made certain changes it should run in a normal way and call inner handler to invoke respective Controller’s action method. Run an application again and paste the following URl in Postman (http://localhost:57888/api2/employees/5). Please see the following screenshot as an example.


    Output will be like this as in the following image:

  2. send
    There are two HttpMessageHandlers pipeline: One is normal pipeline and is applicable for all routes while and second one is Per-Route which is for specific route. The request can skip the pipeline if delegate handler creates the response without calling base.SendAsync, because base.SendAsync requests inner handler which calls HttpRoutingDispatcherand HttpControllerDispatcher and so on.

    You can validate request using Delegatehandler and respond on the basis of them as well as customize the response.

    Hope this article will help you to understand actual facts.
Read more articles on ASP.NET:

Similar Articles