web api从处理程序内部获取路径模板

时间:2014-08-09 19:31:39

标签: c#-4.0 asp.net-web-api asp.net-web-api2 asp.net-web-api-routing

我在搜索问题之前进行了大量搜索,但搜索的越多,我得到的就越混乱。

所以我创建了一个处理程序,我试图得到这样的路径:

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属性,但我可以 在堆栈深处的列表中查看路径。我找到了许多可以用来获取路径的不同方法,但这些方法也评估为null。我对如何完成如此简单的事情感到有点迷茫。我使用自托管进行开发,但将使用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

代码在非自托管环境中运行正常,因此这更像是自托管问题。

4 个答案:

答案 0 :(得分:13)

Web Api仍有很多需要改进的地方。找到一种让这种方法发挥作用的方法很棘手,我只希望这可以节省其他人花费我所有时间。

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

答案 1 :(得分:4)

我遇到了类似的问题,但是能够通过以下方式获取消息处理程序中的路径:

request.GetConfiguration()Routes.GetRouteData(请求).Route.RouteTemplate;

答案 2 :(得分:3)

Marco的答案(如下所示)是正确的,只要使用相同的HttpMethod定义的路线不超过一条。 .First()将获取在该特定ApiController中定义的第一条路线,但这并不能确保它获得正确的路线。如果您使用ControllerContext获取路线,则可以确定您拥有所需的确切端点。

Marco的:

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

答案 3 :(得分:0)

我认为你可以从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;
    }

unlike Python