ASP.NET MVC 5: Przeciążenie atrybutów

W najnowszej wersji 5, mamy do dyspozycji nowy rodzaj filtrów, implementujący interfejs IOverrideFilter:

public interface IOverrideFilter
{
    /// <summary>
    /// Gets the type of filters to override.
    /// </summary>
    /// 
    /// <returns>
    /// The filter to override.
    /// </returns>
    Type FiltersToOverride { get; }
}

Atrybuty przydają się, gdy mamy jeden filtr nałożony globalnie i potem chcemy zmienić zachowanie wyłącznie dla specyficznej akcji. Na przykład, możemy nałożyć Authorize globalnie i potem dla konkretnej akcji zmienić reguły autoryzacji. Innymi słowy, IOverrideFilter umożliwia wyczyszczenie wszystkich filtrów dla konkretnej akcji. Domyślnie mamy następujące implementacje wspomnianego interfejsu:

  1. OverrideAuthenticationAttribute
  2. OverrideAuthorizationAttribute
  3. OverrideActionFiltersAttribute
  4. OverrideResultAttribute
  5. OverrideExceptionAttribute

Jak widać, możemy przeciążyć dowolny typ filtrów. Warto zauważyć OverrideAuthethicationAttribute. Filtry uwierzytelniające zostały dodane również w ASP.NET MVC 5, ale o nich kiedyś indziej…

Załóżmy, że mamy pewien filtr:

public class PerformanceMonitorAttribute : FilterAttribute, IActionFilter
{
   private Stopwatch _timer;

   public void OnActionExecuting(ActionExecutingContext filterContext)
   {
       _timer = Stopwatch.StartNew();
   }

   public void OnActionExecuted(ActionExecutedContext filterContext)
   {
       _timer.Stop();

       filterContext.HttpContext.Response.Write(string.Format("Czas wykonania akcji: {0}", _timer.ElapsedTicks));
   }
}

Został on dodany globalnie:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
  filters.Add(new HandleErrorAttribute());
  filters.Add(new PerformanceMonitorAttribute());
}

Następnie, chcemy go wyłączyć dla konkretnej akcji w HomeController:

[OverrideActionFilters]
public ActionResult Index()
{
  
  return View();
}

Analogicznie możemy przeciążyć Authorize co jest bardziej praktycznym scenariuszem. Na przykład można wymagać pozwoleń do całego kontrolera, a potem przeładować jakąś akcję z innymi parametrami (np. inna grupa). Niestety w wersji 5.0 istnieje bug, który został oficjalnie zatwierdzony. Implementacja atrybutu wygląda następująco:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class OverrideActionFiltersAttribute : Attribute, IOverrideFilter
{
/// <summary>
/// Gets the filters to override for this instance.
/// </summary>
/// 
/// <returns>
/// The filters to override for this instance.
/// </returns>
public Type FiltersToOverride { get; }
}

Aby atrybut działał jak należy, powinno dziedziczyć się po FilterAttribute tzn.:

class CustomOverrideActionFiltersAttribute : FilterAttribute, IOverrideFilter
{
    public Type FiltersToOverride
    {
        get
        {
            return typeof(IActionFilter);
        }
    }
}

Analogicznie sytuacja wygląda z autoryzacją:

public class OverrideAuthorizeAttribute : AuthorizeAttribute, IOverrideFilter
{
    public Type FiltersToOverride
    {
        get { return typeof(IAuthorizationFilter); }
    }
}

W wersji 5.1 zostało to już naprawione i nie trzeba korzystać z własnych atrybutów – te dostarczone dziedziczą po odpowiednich już klasach. Dziwne, że zostało to wydane w takiej postaci, skoro jest to jedna z ważniejszych nowości…

3 thoughts on “ASP.NET MVC 5: Przeciążenie atrybutów”

Leave a Reply

Your email address will not be published.