【问题标题】:ASP.NET MVC Pass Auth info to ViewModel from Controller Method AttributeASP.NET MVC 将身份验证信息从控制器方法属性传递给 ViewModel
【发布时间】:2015-05-19 21:04:32
【问题描述】:

我正在改造一个 asp.net MVC Backoffice 以获得完整的多租户支持。

由于我决定使用分片来实现租户之间的完美分离,因此我需要在每个 viewModel 中访问一些 Auth 信息(租户 ID),只有这些信息我才能为指定的租户创建正确的连接。

为了传递该信息,我有 3 条规则:

  • 我不想使用会话变量
  • 我不想使用 ViewBag
  • 我不想为每个 VM 添加一个新对象以获取身份验证信息

最重要的是,我想要一个“无代码”的解决方案,完美的场景是访问 VM 内的身份验证信息,例如在控制器调用中将其作为属性传递。

我已经重写了 Controller OnAuthorization 以便每次调用它时都会将tenantId 添加到我的基本控制器(这是我所有控制器的基础),这样我总是可以在每个控制器中捕获tenantId,现在我只需要一个将属性中的 TenantId 传递给每个 VM 的方法,类似于以下伪代码

    [Authorize]
    [TenantId AS A PARAM]
    public ActionResult Index()
    {
        myViewModel vm = new myViewModel();
        vm.method();

        return this.View(vm);
    }

【问题讨论】:

    标签: c# asp.net asp.net-mvc asp.net-mvc-4


    【解决方案1】:

    1) 将此TenantId 放入HttpContext.Curent.Items

    2) 编写一个静态函数,从上下文中返回这个TenantId

        private static int GetTenantId()
        {
            return HttpContext.Current.Items["TenantId"];
        }
    

    3) 创建BaseViewModel

    public abstract class BaseViewModel
    {
        public Func<int> GetTenantIdFunc{get;set;}
    }
    

    4) 使用依赖注入容器注册您的GetTenantId 函数并通过属性注入将其注入到您的所有模型中

    【讨论】:

    • 我最终还是使用了 DI,尽管它不是我的第一选择。我没有使用 HttpContext,因为我之前的实现足够强大,可以确保每次调用控制器中的方法时,我都可以直接访问租户 ID
    【解决方案2】:

    一种选择是使用基本视图模型和继承:

    public abstract BaseViewModel()
    {
        public int TenantId { get; set; }
    
        public void SetAuthInfo(BaseController controller)
        {  
            this.TenantId = controller.TenantId;
        }
    }
    
    public MyViewModel() : BaseViewModel
    // no other changes needed to MyViewModel
    ...
    
    public ActionResult Index()
    {
        var model = new MyViewModel();
        model.SetAuthInfo(this);
    
        return View(model);
    }
    

    要通过属性(而不是 model.SetAuthInfo)获取此信息,请添加一个操作过滤器并覆盖 OnResultExecuted 并将其添加到那里,类似于(未经测试):

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public sealed class SetTenantActionAttribute : ActionFilterAttribute
    {
        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            var baseController = filterContext.Controller as BaseController;
            if (baseController == null) return;
    
            var model = filterContext.Controller.ViewData.Model as BaseViewModel;
            if (model == null) return;
    
            model.TenantId = baseController.TenantId;
        }
    }
    

    然后您可以将其添加到您的基本控制器(甚至比添加到每个操作中的代码更少)

    【讨论】:

    • 我喜欢你的建议,只是一个问题,过滤器实现模型将始终为 null ,因为 Controller.ViewData.Model 为 null
    • 实际上使用 OnResultExecuted 是不合适的,因为我希望过滤器在 viewModel 操作之前而不是之后执行
    • 好的 - 我没有尝试过,所以在将模型传递给视图 return this.View(vm); 后,我相信它是空的。不清楚你真正想要什么。
    • 我需要在创建 viewModel 之后但在执行方法之前执行过滤器。我需要在 viewModel 方法中访问 TenantId,以便我可以将我的 dbconnection 重定向到正确的分片
    • 然后使用预编辑版本,只需调用model.SetAuthInfo(this);
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多