19

In an ASP.NET 4.5 MVC 4 Web API project, I want to add a custom HttpMessageHandler. I've changed WebApiConfig class (in \App_Satrt\WebApiConfig.cs), as follows:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional },
            constraints: null,
            handler: new MyCustomizedHttpMessageHandler()
        );
    }
}

Then I developed MyCustomizedHttpMessageHandler:

public class MyCustomizedHttpMessageHandler : HttpMessageHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        IPrincipal principal = new GenericPrincipal(
            new GenericIdentity("myuser"), new string[] { "myrole" });
        Thread.CurrentPrincipal = principal;
        HttpContext.Current.User = principal;

        return Task<HttpResponseMessage>.Factory.StartNew(() => request.CreateResponse());
    }
}

However, the request to the API (let's say http://mylocalhost.com/api/values), always returns status code 200, without any data. I mean it never gets to ValuesController.cs's 'GET()' method.

What have I missed? How can I implement HttpMessageHandler properly?

PS: Have already read this one: https://stackoverflow.com/a/12030785/538387 , doesn't help me.

Community
  • 1
  • 1
Tohid
  • 6,175
  • 7
  • 51
  • 80

3 Answers3

24

Here you are creating a HttpMessageHandler which short circuits the request and doesn't let the request pass through the rest of the pipeline. Instead, you should create a DelegatingHandler.

Also there are 2 kinds of message handler pipelines in Web API. One is a regular pipeline in which all requests for all routes pass through and other where one could have message handlers specific to certain routes only.

  1. Try to create a DelegatingHandler and add it to your HttpConfiguration's list of message handlers:

    config.MessageHandlers.Add(new HandlerA())
    
  2. If you want to add a route specific message handler, then you could do the following:

    config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional },
                constraints: null,
                handler: 
                       HttpClientFactory.CreatePipeline(
                              new HttpControllerDispatcher(config), 
                              new DelegatingHandler[]{new HandlerA()})
                );
    

This Web Api Poster shows the pipeline flow.

Thymine
  • 8,775
  • 2
  • 35
  • 47
Kiran
  • 56,921
  • 15
  • 176
  • 161
  • Thank you. Solution (1) works well. But number 2, `new HttpControllerDispatcher()` says: **System.Web.Http.Dispatcher.HttpControllerDispatcher does not contain a constructor that takes 0 arguments** - Update: I fixed your code. Thank you. – Tohid Mar 04 '13 at 17:43
  • Please also add this part of Cuong Le's answer to your answere: **To write a custom message handler, you should derive from `System.Net.Http.DelegatingHandler`.** – Tohid Mar 04 '13 at 18:37
  • The link for the poster is broken. – 0xcaff Jan 04 '16 at 20:10
  • in which file do we need to add the code: config.MessageHandlers.Add(new HandlerA()) – William Nov 24 '16 at 02:04
15

To write a custom message handler, you should derive from System.Net.Http.DelegatingHandler

class CustomMessageHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> 
      SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        IPrincipal principal = new GenericPrincipal(
            new GenericIdentity("myuser"), new string[] { "myrole" });
        Thread.CurrentPrincipal = principal;
        HttpContext.Current.User = principal;

        return base.SendAsync(request, cancellationToken);
    }
}

And call base.SendAsync to send the request to the inner handler.

cuongle
  • 74,024
  • 28
  • 151
  • 206
  • 4
    @Cuonge Le - Thank you for the answer. After I replaced `CustomMessageHandler` with your code, it raises error on `return base.SendAsync(request, cancellationToken);` saying: **The inner handler has not been assigned.** . What I missed again? – Tohid Mar 04 '13 at 17:27
  • @Counge Le - The combination of your answer and Kirran Chall's, has fixed the issue. Thank you again. – Tohid Mar 04 '13 at 17:57
  • @Cuong Le what is the equivalent for .NET 4.0? I could not find DelegatingHandler for .NET .4... how can do messagehandling in 4.0. Thanks for your help. – jaxxbo May 22 '13 at 15:06
0

I used @cuongle answer to solve my problem. Just with one addition. So I did not get "The inner handler has not been assigned.". Thank you @coungle.

public class CustomMessageHandler : DelegatingHandler
{
        public CustomMessageHandler ()
        {
              //add this to solve "The inner handler has not been assigned"
               InnerHandler = new HttpClientHandler();
        }
    
        protected override async Task<HttpResponseMessage> 
          SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            // Before request 
            IPrincipal principal = new GenericPrincipal(
                new GenericIdentity("myuser"), new string[] { "myrole" });
            Thread.CurrentPrincipal = principal;
            HttpContext.Current.User = principal;
    
            var result = await base.SendAsync(request, cancellationToken);
    
            // After request when response arrives
            
            return result;
        }
}
Bahtiyar Özdere
  • 1,818
  • 17
  • 21