在Asp.net Web Api授权过滤器中,如何访问参数?

14

我正在使用Asp.Net Web API,遇到了以下问题:

我实现了一个自定义的授权过滤器来检查消息头,寻找API密钥。根据这个API密钥,我获取用户信息,然后想查看他是否可以访问某些资源。我要检查的资源ID在HTTP请求的参数中。但当我在AuthorizationFilter方法中时,操作参数列表是空的。

我该如何解决?

如果我使用ActionFilter来替换授权过滤器,那么我怎样才能确保这是第一个执行的过滤器?并且,我该如何指定过滤器的执行顺序?

最后一个问题,有没有可能添加一些“管道”数据,我可以在任何过滤器中检索这些数据?类似于会话存储,但仅限于请求?

谢谢您的回复。

2个回答

24

授权属性在参数绑定之前运行,因此您无法(如您所见)使用ActionArguments集合。相反,您需要使用请求URI进行查询参数和路由数据进行URI参数,如下所示。

//request at http://localhost/api/foo/id?MyValue=1
public class MyAuthorizationAttribute : AuthorizeAttribute
{
    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        //will not work as parameter binding has not yet run
        object value;
        actionContext.ActionArguments.TryGetValue("id", out value);

        //Will get you the resource id assuming a default route like /api/foo/{id} 
        var routeData = actionContext.Request.GetRouteData();
        var myId = routeData.Values["id"] as string;

        //uri is still accessible so use this to get query params
        var queryString = HttpUtility.ParseQueryString(actionContext.Request.RequestUri.Query);
        var myQueryParam = queryString["MyValue"];

        //and so on
    }
}

关于执行顺序:

使用FilterScope枚举有三种不同的方法来指定过滤器的执行顺序...它们的作用域分别是Global、Controller和Action。 AuthoriseAttribute是“全局”的,因此它在控制器之前指定了一个操作。

  

在控制器之前指定一个操作。

如果需要在这3个范围内指定执行顺序,则应阅读此博客文章,其中您需要实现一个FilterProvider

向管道添加一些数据:

使用请求上的属性集合,该集合可用于整个请求的持续时间。

    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        actionContext.Request.Properties.Add("__MYKEY__","MyValue");

        //access this later in the controller or other action filters using
        var value = actionContext.Request.Properties["__MYKEY__"];

    }

您还可以从RouteData中获取路径参数信息。如果我没记错的话,它应该是类似于Request.GetRouteData().Values; - Darrel Miller
@DarrelMiller,我刚刚跟进了那个想法,但我的测试工具似乎无法以那种方式工作。var routeData = actionContext.Request.GetRouteData(); var value = routeData.Values["MyValue"] as string; 我的routeData只包含控制器值。如果您不同意,请告诉我,我会进一步调查。 - Mark Jones
先使用授权过滤器将一个值设置到请求属性中,然后再使用操作过滤器是有效的。其他信息也很有用。谢谢您的帮助。 - Ben
@MarkJones 我刚刚尝试了使用默认的/api/values/{id}路由,并且我能够在AuthorizeAttribute的OnAuthorization事件中从RouteData中读取Id。 - Darrel Miller
@DarrelMiller 我认为这个问题是关于查询字符串参数的。不过我同意你的建议,Id应该是可用的。 - Mark Jones
@DarrelMiller,那是我误读了问题。抱歉。我会更新以澄清事情。 - Mark Jones

1

另一种获得参数的替代方法是对参数绑定执行Execute

try
{
    var binding = actionContext.ActionDescriptor.ActionBinding;
    var parameters = binding.ParameterBindings.OfType<ModelBinderParameterBinding>();
    var newBinding = new HttpActionBinding(actionContext.ActionDescriptor, parameters.ToArray());
    newBinding.ExecuteBindingAsync(actionContext, new CancellationToken());
    var id = actionContext.ActionArguments["id"] as string;
}
catch
{
    base.HandleUnauthorizedRequest(actionContext);
}

注意:您需要确保仅过滤请求URI中的参数,因为我已经注意到执行绑定的任何预期来自请求正文的参数将不再传递给实际操作。即这些参数将为null。这只是要注意您可以这样做,我建议使用GetRouteData()/RouteData,因为它不太可能干扰ASP.NET MVC模型绑定的进一步流程。
var routeData = actionContext.ControllerContext.RouteData;
var id = routeData.Values["id"] as string;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接