问题
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?
回答1:
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?
回答2:
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 );
来源:https://stackoverflow.com/questions/15046073/accessing-apicontroller-type-in-delegatinghandler