在将问题放在这里之前我进行了很多搜索,但搜索得越多,我就越困惑。
所以我创建了一个处理程序,我试图获得这样的路线:
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 返回 null 所以我无法访问 RouteTemplate 属性但我可以 在堆栈深处的列表中查看那里的路线。我发现了很多可以用来获取路线的不同方法,但这些方法的计算结果也为空。我对如何完成如此简单的事情有点迷茫。我正在使用自我主机进行开发,但将使用 IIS 进行部署。
更新 1
我忘了把我还试过的东西放在这里:
//NULL
request.GetRouteData();
//EMPTY
request.GetRequestContext().Configuration.Routes.GetRouteData(request).Route.RouteTemplate;
//EMPTY
request.GetConfiguration().Routes.GetRouteData(request).Route.RouteTemplate;
路线工作得很好,但奇怪的是,如果我试图让控制器为该请求提供服务,我会得到一个 404 ......如果我只是跨过它,我会很好地到达控制器。
HttpControllerDescriptor httpControllerDescriptor = request.GetRequestContext().Configuration.Services.GetHttpControllerSelector().SelectController(request);
IHttpController httpController = httpControllerDescriptor.CreateController(request);
我正在使用 autofac 来发现我定义的所有路线,就像:
[Route("queries/organization/clients")]
[HttpGet]
public ClientInitialScreenModel GetClients()
{
return OrganizationModelsBuilder.GetClientInitialScreen();
}
更新 2
如果我在上面一行之后调用 GetRouteData,我就能得到路线模板:
base.SendAsync(request, cancellationToken);
var routeData = request.GetRouteData();
所以也许我误解了整个画面,我无法在解决为请求执行哪个控制器的处理程序工作之前获取路由模板......是这样吗?
供参考,这是我正在处理的处理程序:
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;
}
更新 3
代码在非自托管环境中运行良好,所以这更像是一个自托管问题。
Web Api 还有很多需要改进的地方。找到一种方法让这个工作起来很棘手,我只是希望这可以让其他人免于花费我所有的时间。
var routeTemplate = ((IHttpRouteData[]) request.GetConfiguration().Routes.GetRouteData(request).Values["MS_SubRoutes"])
.First().Route.RouteTemplate;
我有一个类似的问题,但能够通过以下方式在消息处理程序中获取路由:
request.GetConfiguration().Routes.GetRouteData(请求).Route.RouteTemplate;
Marco 的回答(如下所示)是正确的,只要不超过一个路由定义了相同的 HttpMethod。
.First()
将获取特定 ApiController
中定义的第一条路线,但这并不能确保它获取正确的路线。如果您使用 ControllerContext
获取路线,您可以确定您已经获得了您想要的确切端点。
马可的:
var routeTemplate = ((IHttpRouteData[])request.GetConfiguration()
.Routes.GetRouteData(request).Values["MS_SubRoutes"])
.First().Route.RouteTemplate;
代码:
((IHttpRouteData[])request.GetConfiguration()
.Routes.GetRouteData(request).Values["MS_SubRoutes"])
实际上返回
IHttpRouteData
的集合,它包含每个具有相同HttpMethod(Post,Get等)的端点的记录.......First()
不能保证你得到你想要的那个。
保证抓取正确端点的
RouteTemplate
:
public static string GetRouteTemplate(this HttpActionContext actionContext)
{
return actionContext.ControllerContext.RouteData.Route.RouteTemplate;
}
我使用了一种扩展方法,因此您可以这样称呼它:
var routeTemplate = actionContext.GetRouteTemplate();
这将确保您从拨打电话的端点获得特定的
RouteTemplate
。
我认为您可以从 request.Properties 属性获取路由数据并且易于单元测试。
/// <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;
}
var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template ?? "Unknown";