Search code examples
c#-4.0asp.net-web-apiasp.net-web-api2asp.net-web-api-routing

web api get route template from inside handler


I searched a lot before putting the questions here but the more I search the more confused I get.

So I have created an handler and I am trying to get the route like this:

public class ExecutionDelegatingHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (securityAuthority.VerifyPermissionToExecute(request.GetRouteData().Route.RouteTemplate, request.Headers))
        {
            return base.SendAsync(request, cancellationToken);
        }
        else
        {
            httpResponseMessage.StatusCode = HttpStatusCode.Unauthorized;
        }
    }
 }

GetRouteData returns null so I can't get to the RouteTemplate property but I can see the route there in a list deep in the stack. I found so many different ways which one can use to get the route, but those methods evaluate to null as well. I am a bit lost on how to get something so simple done. I am using self host for development but will use IIS for deployment.

UPDATE 1

I forgot to put here what else I had tried:

//NULL
request.GetRouteData(); 
//EMPTY
request.GetRequestContext().Configuration.Routes.GetRouteData(request).Route.RouteTemplate;
//EMPTY
request.GetConfiguration().Routes.GetRouteData(request).Route.RouteTemplate;

The route works just fine, but strangely if I try to get the controller to service that request I get a 404... if I just step over that I will get to the controller just fine.

HttpControllerDescriptor httpControllerDescriptor = request.GetRequestContext().Configuration.Services.GetHttpControllerSelector().SelectController(request);
IHttpController httpController = httpControllerDescriptor.CreateController(request);

I am using autofac to discover all the routes which I am defining just like:

[Route("queries/organization/clients")]
[HttpGet]
public ClientInitialScreenModel GetClients()
{
    return OrganizationModelsBuilder.GetClientInitialScreen();
}

UPDATE 2

If I GetRouteData gets called after the line above, I am able to get the route template:

base.SendAsync(request, cancellationToken);

var routeData = request.GetRouteData();

So maybe I misunderstood the whole picture and I cant get the route template before the handler that resolves which controller to execute for the request does its work... is that the case?

For reference this is the handler I am working on:

public class ExecutionDelegatingHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var securityAuthority = (ISecurityAuthority) request.GetDependencyScope().GetService(typeof (ISecurityAuthority));
        var configuration = (IWebApiConfiguration)request.GetDependencyScope().GetService(typeof(IWebApiConfiguration));
        var tsc = new TaskCompletionSource<HttpResponseMessage>();
        var httpResponseMessage = new HttpResponseMessage();

        if (request.RequestUri.AbsolutePath.Equals(configuration.CommandGatewayUrl, StringComparison.InvariantCultureIgnoreCase))
        {
            var apiMessage = JsonConvert.DeserializeObject<ApiCommandEnvelope>(request.Content.ReadAsStringAsync().Result);

            if (securityAuthority != null && !securityAuthority.VerifyPermissionToExecute(apiMessage, request.Headers))
            {
                httpResponseMessage.StatusCode = HttpStatusCode.Unauthorized;
            }
            else
            {
                var messageProcessor = (IWebApiMessageProcessor)request.GetDependencyScope().GetService(typeof(IWebApiMessageProcessor));
                var reponse = messageProcessor.HandleRequest(apiMessage);

                httpResponseMessage.StatusCode = (HttpStatusCode) reponse.StatusCode;

                if (!string.IsNullOrEmpty(reponse.Content))
                {
                    httpResponseMessage.Content = new StringContent(reponse.Content);
                }
            }
        }
        else
        {
            if (securityAuthority != null && !securityAuthority.VerifyPermissionToExecute(request.GetRouteData().Route.RouteTemplate, request.Headers))
            {
                httpResponseMessage.StatusCode = HttpStatusCode.Unauthorized;
            }
            else
            {
                return base.SendAsync(request, cancellationToken);
            }
        }

        tsc.SetResult(httpResponseMessage);

        return tsc.Task;
    }

UPDATE 3

The code runs fine in a non self hosting environment, so this is more like a self host issue.


Solution

  • The Web Api still has a lot to improve. It was tricky to find a way to get this working and I just hope this saves other guys from spending all the time I did.

     var routeTemplate = ((IHttpRouteData[]) request.GetConfiguration().Routes.GetRouteData(request).Values["MS_SubRoutes"])
                                        .First().Route.RouteTemplate;