Как передать метод или параметр в фильтр действий в ASP.Net MVC - PullRequest
0 голосов
/ 26 декабря 2018

Я собираюсь обработать аутентификацию и авторизацию в фильтре действий и создать фильтр действий, как показано ниже:

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. Как передать AuthFunc (метод Courses.CheckCoursesOfTeacher(CourseId)) в фильтр действий?

  2. Как получить CourseId параметр действия в атрибуте фильтра действия (передать CourseId или ExerciseId в качестве значения атрибута)?

    Каков наилучший способ решения этих проблем (функциии переменные не могут быть отправлены в фильтр действий)?

1 Ответ

0 голосов
/ 01 июля 2019

Проблема с передачей функций в параметрах атрибута

Я недавно нашел решение, подобное этому.Параметры для атрибутов должны соответствовать следующим правилам для MS Docs :

Параметры для конструктора атрибутов ограничены простыми типами / литералами: bool, int, double, string,Тип, перечисления и т. Д. И массивы этих типов.Вы не можете использовать выражение или переменную.Вы можете использовать позиционные или именованные параметры.

Из-за этого передача функции фильтру через параметр атрибута - это не то, что мы можем сделать.Вероятно, существует множество альтернатив, но вот что я выбрал:

Решение

Я использовал внедрение зависимостей , чтобы внедрить свой фильтр действий с менеджером, а затемиспользовал Reflection , чтобы указать фильтру, какой метод в менеджере выполнить.Вот как это выглядит:

Класс:

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);

Последним, что мне потребовалось некоторое время, был DI для фильтра, использующего Ninject.Вот как это работает в моем

Global.asax.cs:

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

Сводка

Вместо передачи функции в качестве параметра атрибута, я использовал DI, чтобы ввестименеджер в мой фильтр.Это дает вашему фильтру доступ к нужным функциям.Во-вторых, я использовал enum для хранения имени функции, которая должна быть выполнена.По сути, все, что вам нужно сделать, чтобы создать новую функцию и выполнить ее с параметром, это:

  1. Добавить ее в перечисление
  2. Добавить функцию с тем же именем вменеджер

Надеюсь, это поможет!

Дальнейшее чтение

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

...