如何在ASP.NET Core 2.0中访问服务中的Route Data / Value Provider数据?

时间:2018-02-15 17:01:59

标签: c# asp.net-mvc asp.net-core dependency-injection asp.net-core-2.0

我正在尝试写一个Policy-based Authorization Handler。处理程序的业务逻辑需要使用通过默认路由传递的当前请求的记录id

[Authorize(Roles = "TaskAdmin", Policy = "RecordOwner")]
public IActionResult Index(int id) // <-- Need this id
{
    // <snip>

    return View();
}

政策

以下是我需要访问id路由值的类。

public class RecordOwnerHandler : AuthorizationHandler<RecordOwnerRequirement>
{
    private readonly ApplicationDbContext dbContext;

    public RecordOwnerHandler(ApplicationDbContext dbContext)
    {
        this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RecordOwnerRequirement requirement)
    {
        if (IsUserAuthorized(context))
        {
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }

    private bool IsUserAuthorized(AuthorizationHandlerContext context)
    {
        //****************************************
        // Need the id here...
        //****************************************

        // Return the result
        return true;
    }
}

启动

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    // *** Add policy for record owner ***
    services.AddAuthorization(options =>
    {
        options.AddPolicy("RecordOwner", policy =>
            policy.Requirements.Add(new RecordOwnerRequirement()));
    });

    // Add application services.
    services.AddTransient<IEmailSender, EmailSender>();

    // *** Register record owner handler with the DI container ***
    services.AddTransient<IAuthorizationHandler, RecordOwnerHandler>(); 

    services.AddMvc();
}

我尝试了什么

  1. 我尝试使用IHttpContextAccessor作为RecordOwnerHandler的构造函数参数,但IHttpContextAccessor.HttpContext似乎不包含请求的RouteData

  2. 我进行了几次Google搜索,看看是否有任何关于如何执行此操作的信息,并且显示空白。

  3. 然后我挖掘了路由模型绑定的源代码,但似乎找不到用于注入路由值的抽象进入服务。

  4.   

    我意识到我可以尝试从URL中解析这些信息,但我希望有一种更清晰的方法来获取价值。

    那么,如何在ASP.NET Core 2.0中访问服务中的路由值和/或值提供程序数据?

4 个答案:

答案 0 :(得分:7)

可以使用ActionContextAccessor class

访问路线值

DI注册

services.AddTransient<IActionContextAccessor, ActionContextAccessor>();

用法

public class RecordOwnerHandler : AuthorizationHandler<RecordOwnerRequirement>
{
    private readonly ApplicationDbContext dbContext;
    private readonly IActionContextAccessor actionContextAccessor;

    public RecordOwnerHandler(ApplicationDbContext dbContext, IActionContextAccessor actionContextAccessor)
    {
        this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
        this.actionContextAccessor = actionContextAccessor ?? throw new ArgumentNullException(nameof(actionContextAccessor));
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RecordOwnerRequirement requirement)
    {
        if (IsUserAuthorized(context))
        {
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }

    private bool IsUserAuthorized(AuthorizationHandlerContext context)
    {
        // Now the id route value can be accessed directly...
        var id = this.actionContextAccessor.ActionContext.RouteData.Values["id"];

        // Use the dbContext to compare the id against the database...

        // Return the result
        return true;
    }
}
  

注意:我仍然想找到一种方法来访问价值提供商来做到这一点,所以如果参数通过路由值,查询字符串传递,那就不重要了形式值等。

答案 1 :(得分:2)

为了将来参考,从 .NET Core 5.0 开始,现在传递 HttpContext,因此您可以执行以下操作:

if (context.Resource is HttpContext httpContext)
{
   var value = httpContext.GetRouteValue("key");
}

请参阅此参考[AspNetCore] Authorization resource in endpoint routing will now be HttpContext

答案 2 :(得分:1)

不了解.NET Core 2.0,但是在3.0中,您可以借助IHttpContextAccessor访问路由值,该服务实现将由框架自动为您提供(因此无需注册任何内容。)

public class RecordOwnerHandler : AuthorizationHandler<RecordOwnerRequirement>
{
    private readonly ApplicationDbContext dbContext;
    private readonly IHttpContextAccessor httpContextAccessor;

    public RecordOwnerHandler(ApplicationDbContext dbContext, IHttpContextAccessor httpContextAccessor)
    {
        this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
        this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RecordOwnerRequirement requirement)
    {
        if (IsUserAuthorized(context))
        {
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }

    private bool IsUserAuthorized(AuthorizationHandlerContext context)
    {
        if (!_httpContextAccessor.HttpContext.Request.RouteValues.TryGetValue("id", out var id))
        {
            return false;
        }

        // TODO Whatever logic you need to perform with the id

        return true;
    }
}

答案 3 :(得分:0)

我有相同的用例,并尝试通过 AuthorizationHandler 中的端点元数据访问路由值,如 ASP.NET Core 5 安全文档中 Access MVC request context in handlers 部分的建议:

AgGridReact

但是 if (!(context.Resource is Endpoint endpoint)) throw new InvalidOperationException("endpoint routing required"); var id = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>()?.RouteValues["id"]; context.Resource 类型,所以我不得不这样做:

DefaultHttpContext