簡體   English   中英

如何將方法或參數傳遞給ASP.Net MVC中的操作篩選器

[英]How to pass a method or parameter to an action filter in ASP.Net MVC

我將在操作過濾器中處理身份驗證和授權,並創建一個如下所示的操作過濾器:

public class Auth : ActionFilterAttribute
{
    public int Access { get; set; }
    public string Roles { get; set; } = "Default";
    public Func<bool> AuthFunc { get; set; }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        string UserId = HttpContext.Current.User.Identity.GetUserId();
        //Authentication 
        if (Roles != "Default" && UserManager.IsInRole(UserId, Roles))
        {
           //Authorization 
           if (AuthFunc) { base.OnActionExecuting(actionContext); }
           else
             {
                var response = actionContext.Request.CreateResponse(HttpStatusCode.Redirect);
                Uri requestUrl = actionContext.Request.RequestUri;
                response.Headers.Location = new Uri($"{requestUrl.Scheme}://{requestUrl.Host}:{requestUrl.Port}");
                actionContext.Response = response;
             }
        }
        else
        {
            var response = actionContext.Request.CreateResponse(HttpStatusCode.Redirect);
            Uri requestUrl = actionContext.Request.RequestUri;
            response.Headers.Location = new Uri($"{requestUrl.Scheme}://{requestUrl.Host}:{requestUrl.Port}");
            actionContext.Response = response;
        }
    }
}

並在控制器中:

[Auth(Roles="Teacher" , Access = (short)TableEnum.Course , AuthFunc = Courses.CheckCoursesOfTeacher(CourseId))]
public ActionResult ShowExerciseAnswers(int CourseId,int ExerciseId)
{
    return View(model: ChapterExerciseAnswer.ExerciseAnswerList(CourseId,ExerciseId));
}

AuthFunc方法可能具有多個輸入,但只有bool返回值。

  1. 如何將AuthFuncCourses.CheckCoursesOfTeacher(CourseId)方法)傳遞給動作過濾器?

  2. 如何在動作過濾器屬性中獲取CourseId動作參數(將CourseIdExerciseId作為屬性值傳遞)?

    處理這些問題的最佳方法是什么(不能將函數和變量發送到動作過濾器)?

屬性參數傳遞函數的問題

我最近發現自己正在尋找這樣的解決方案。 每個MS Docs的屬性參數必須遵循以下規則:

屬性構造函數的參數限於簡單的類型/文字:bool,int,double,string,Type,enums等以及這些類型的數組。 您不能使用表達式或變量。 您可以自由使用位置或命名參數。

因此,我們無法通過屬性參數將函數傳遞給過濾器。 可能有很多選擇,但是這是我選擇要做的:

一個解法

我使用依賴項注入向管理器注入了動作過濾器,然后使用了反射來告訴過濾器在管理器上執行哪個方法。 看起來是這樣的:

類:

public class Phone : AuditBase 
{
    ...other props...

    [AuditRuleset(AuditRule.PhoneNumber)]
    public string Number { get; set; }
}

枚舉:

public enum AuditRule
{
    PhoneNumber // You can add [Description] if you want
}

屬性:

public class AuditRulesetAttribute : Attribute
{
    private readonly AuditRule _rule;

    public AuditRulesetAttribute(AuditRule rule) => _rule = rule;
}

過濾:

public class FormAuditActionFilter : IActionFilter
{
    private ILog _log { get; set; }
    private IFormAuditor _auditor { get; set; }

    public FormAuditActionFilter(ILog log, IFormAuditor auditor)
    {
        _log = log;
        _auditor = auditor;
    }
    ...lots of filter code...
    ... The following is from OnActionExecuted, having stored the props of the submitted object in objectProperties...

    foreach(PropertyInfo propertyInfo in objectProperties)
    {
        // Check first for any special audit comparison rules which should be followed
        var auditRuleAttributes = propertyInfo.CustomAttributes
            .Where(x => x.AttributeType.Name == typeof(AuditRulesetAttribute).Name)
            .ToList();

        if (auditRuleAttributes.Any())
        {
            IEnumerable<IList<CustomAttributeTypedArgument>> attrList = auditRuleAttributes
                .Select(x => x.ConstructorArguments);

            foreach(IList<CustomAttributeTypedArgument> attr in attrList)
                foreach(CustomAttributeTypedArgument arg in attr)
                    if (_auditRuleManager.IsChanged(oldValue, newValue, (AuditRule)arg.Value))
                        result.Add(BuildValueHistory(propertyInfo.Name, oldValue, newValue));
            continue;
        }
    }
    ...lots more filter code...
}

AuditRuleManager:

public class AuditRuleManager : IAuditRuleManager
{
public bool IsChanged(object val1, object val2, AuditRule rule)
{
    object[] objArray = {val1, val2};

    var comparisonResult = typeof(AuditRuleManager)
        .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
        .Single(m => m.Name == rule.GetDescription()) // Try to get description, but falls back to name by default
        .Invoke(this, objArray) as bool?;

    return (bool)comparisonResult; // Throw an exception if the comparison result was not a valid bool
}

// Compare phone numbers with special rules, and return their equality
private bool PhoneNumber(object val1, object val2) // NOTE: Name of method matches name of enum value
    => GetNumbersFromString(val1 as string) != GetNumbersFromString(val2 as string);

最后花了我一段時間的是使用Ninject的濾波器的DI。 這是我的工作方式

的Global.asax.cs:

kernel.BindFilter<FormAuditActionFilter>(FilterScope.Action, 0)
                  .WhenActionMethodHas<FormAuditAttribute>()
                  .WithConstructorArgument("log", log)
                  .WithConstructorArgument("auditor", auditManager);

摘要

我沒有使用函數作為屬性參數,而是使用DI將管理器注入到過濾器中。 這使您的過濾器可以訪問所需的功能。 其次,我使用了一個枚舉來保存應執行的函數的名稱。 因此,從本質上講,創建新函數並使用參數執行該函數所要做的就是:

  1. 將其添加到枚舉
  2. 向管理器添加同名功能

我希望這有幫助!

進一步閱讀

https://blogs.cuttingedge.it/steven/posts/2014/dependency-injection-in-attributes-dont-do-it/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM