Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing ApiController Type in DelegatingHandler

I am using Asp.Net WebAPI for a project. I am currently working on authentication and authorization.

I have a messageHandler that will check the HTTP authentication header of a request and build my identity and user profile. However, I want to annotate my controller action (or just the controller) with claims that the action may require (we have a lot of claims that a user can have, so I don't want to load them all).

e.g.:

public class MyController : ApiController
{
    [LoadClaims("SomeClaim", "SomeOtherClaim", "etc")]
    public string Get()
    {
        if (HasClaim("SomeClaim"))
            return "Awesome";

        return "Bummer";
    }
}

Inside the authentication message handler I want to be able to look at the attributes and bring claims back from the DB based on only what is required. For that I need to know what Controller and Action I will hit based on route:

 protected override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
        {
...

            var routeData = request.GetRouteData();
            object controllerName;
            object actionName;
            routeData.Values.TryGetValue("controller", out controllerName);
...

So I can get that. But now I need to turn this into a Type that I can reflect on, but all I have is the controller name (not even the full class name or namespace). How can I turn this into something that I can reflect on to get attributes etc?

I am looking into DefaultHttpControllerSelector to see how the WebAPI stack does it, and it seems to use HttpControllerTypeCache. This is an internal class so I can't create an instance. What is the correct way to go about getting the target controller Type?

like image 992
Simon C Avatar asked Feb 23 '13 21:02

Simon C


People also ask

Can Apicontroller return view?

You can return one or the other, not both. Frankly, a WebAPI controller returns nothing but data, never a view page. A MVC controller returns view pages.

What is DelegatingHandler C#?

Typically, a series of message handlers are chained together. The first handler receives an HTTP request, does some processing, and gives the request to the next handler. At some point, the response is created and goes back up the chain. This pattern is called a delegating handler.

How can custom handlers be added to HttpClient?

Adding Message Handlers to the Client Pipeline HttpClient client = HttpClientFactory. Create(new Handler1(), new Handler2(), new Handler3()); Message handlers are called in the order that you pass them into the Create method. Because handlers are nested, the response message travels in the other direction.

What is pipeline in Web API?

Web API Delegate Handler Further it travels in pipeline as HttpRequestMessage in Pipeline. They process HTTP request messages on the way in, and HTTP response messages on the way out. To create a custom message handler, derive from the DelegatingHandler class. You can add multiple message handlers.


2 Answers

You can get access to the type resolver yourself using the global service locator.

var controllerTypeResolver = GlobalConfiguration.Configuration.Services.GetHttpControllerTypeResolver();
var controllerTypes = controllerTypeResolver.GetControllerTypes(GlobalConfiguration.Configuration.Services.GetAssembliesResolver());
var controllerType = controllerTypes.SingleOrDefault(ct => ct.Name == string.Format("{0}Controller", controllerName));

You will probably want to do some caching of the results (like the controller selector does). But this approach should work.

But

You may be better off moving this logic into a Custom authorisation filter that sits on the controller rather than a delegating handler. Given you need to know the controller type you may as well let the ControllerSelector work normally. Perhaps, if you turned your load claims attribute into an authorization action filter attribute you could just load the claims passed in as parameters and set the principal and claims there and then?

like image 149
Mark Jones Avatar answered Sep 21 '22 13:09

Mark Jones


If you are still set on the DelegatingHandler you could get the Controller Selector instance itself, which'll be way more efficient:

var controllerSelector = GlobalConfiguration.Configuration.Services.GetHttpControllerSelector();
var controllerDescriptor = controllerSelector.SelectController( request );
like image 25
Jaans Avatar answered Sep 19 '22 13:09

Jaans