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.
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;
I had a similar issue, but was able to get the route inside the message handler by the following:
request.GetConfiguration().Routes.GetRouteData(request).Route.RouteTemplate;
The answer from Marco (shown below) is correct so long as there isn't more than one route defined with the same HttpMethod. The .First()
will grab the 1st route defined in that specific ApiController
, but this doesn't ensure it grabs the correct one. If you use the ControllerContext
to get the Route, you can be sure you've got the exact endpoint you want.
Marco's:
var routeTemplate = ((IHttpRouteData[])request.GetConfiguration()
.Routes.GetRouteData(request).Values["MS_SubRoutes"])
.First().Route.RouteTemplate;
The code:
((IHttpRouteData[])request.GetConfiguration()
.Routes.GetRouteData(request).Values["MS_SubRoutes"])
actually returns a collection of IHttpRouteData
, and it contains a record for each endpoint which has the same HttpMethod (Post, Get, etc)... The .First()
doesn't guarantee you get the one you want.
Guaranteed To Grab Correct Endpoint's RouteTemplate
:
public static string GetRouteTemplate(this HttpActionContext actionContext)
{
return actionContext.ControllerContext.RouteData.Route.RouteTemplate;
}
I used an extension method so to call this you'd do:
var routeTemplate = actionContext.GetRouteTemplate();
This will assure that you get the specific RouteTemplate
from the endpoint making the call.
I think you can get route Data from request.Properties property and easy to unit test.
/// <summary>
/// Gets the <see cref="System.Web.Http.Routing.IHttpRouteData"/> for the given request or null if not available.
/// </summary>
/// <param name="request">The HTTP request.</param>
/// <returns>The <see cref="System.Web.Http.Routing.IHttpRouteData"/> or null.</returns>
public static IHttpRouteData GetRouteData(this HttpRequestMessage request)
{
if (request == null)
{`enter code here`
throw Error.ArgumentNull("request");
}
return request.GetProperty<IHttpRouteData>(HttpPropertyKeys.HttpRouteDataKey);
}
private static T GetProperty<T>(this HttpRequestMessage request, string key)
{
T value;
request.Properties.TryGetValue(key, out value);
return value;
}
来源:https://stackoverflow.com/questions/25222277/web-api-get-route-template-from-inside-handler