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?
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.
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.
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.
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.
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?
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 );
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With