Metody filtrowania stron Razor w ASP.NET Core

Autor: Rick Anderson

Razor Filtry IPageFilter stron i IAsyncPageFilter zezwalaj Razor na uruchamianie kodu przez strony przed uruchomieniem i po uruchomieniu Razor programu obsługi strony. Razor Filtry stron są podobne do filtrów akcji ASP.NET Core MVC, z wyjątkiem tego, że nie można ich stosować do poszczególnych metod obsługi stron.

Razor Filtry stron:

  • Uruchom kod po wybraniu metody obsługi, ale przed wystąpieniem powiązania modelu.
  • Uruchom kod przed wykonaniem metody obsługi po zakończeniu powiązania modelu.
  • Uruchom kod po wykonaniu metody obsługi.
  • Można zaimplementować na stronie lub globalnie.
  • Nie można zastosować do określonych metod obsługi stron.
  • Może mieć zależności konstruktora wypełnione przez wstrzykiwanie zależności (DI). Aby uzyskać więcej informacji, zobacz ServiceFilterAttribute i TypeFilterAttribute.

Podczas gdy konstruktory stron i oprogramowanie pośredniczące umożliwiają wykonywanie kodu niestandardowego przed wykonaniem metody obsługi, tylko Razor filtry strony umożliwiają dostęp do HttpContext i strony. Oprogramowanie pośredniczące ma dostęp do elementu HttpContext, ale nie do "kontekstu strony". Filtry mają FilterContext parametr pochodny, który zapewnia dostęp do HttpContextelementu . Oto przykład filtru strony: Zaimplementuj atrybut filtru, który dodaje nagłówek do odpowiedzi, czego nie można zrobić z konstruktorami lub oprogramowaniem pośredniczącym. Dostęp do kontekstu strony, który obejmuje dostęp do wystąpień strony i jej modelu, są dostępne tylko podczas wykonywania filtrów, procedur obsługi lub treści Razor strony.

Wyświetl lub pobierz przykładowy kod (jak pobrać)

Razor Filtry stron zapewniają następujące metody, które można zastosować globalnie lub na poziomie strony:

  • Metody synchroniczne:

  • Metody asynchroniczne:

Zaimplementuj synchroniczną lub asynchroniczną wersję interfejsu filtru, a nie obie. Struktura najpierw sprawdza, czy filtr implementuje interfejs asynchroniczny, a jeśli tak, wywołuje to. Jeśli nie, wywołuje metody interfejsu synchronicznego. Jeśli oba interfejsy są implementowane, wywoływane są tylko metody asynchroniczne. Ta sama reguła ma zastosowanie do przesłonięć na stronach, zaimplementować synchroniczną lub asynchroniczną wersję przesłonięcia, a nie obie.

Implementowanie Razor filtrów stron globalnie

Poniższy kod implementuje IAsyncPageFilterelement :

public class SampleAsyncPageFilter : IAsyncPageFilter
{
    private readonly IConfiguration _config;

    public SampleAsyncPageFilter(IConfiguration config)
    {
        _config = config;
    }

    public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
    {
        var key = _config["UserAgentID"];
        context.HttpContext.Request.Headers.TryGetValue("user-agent",
                                                        out StringValues value);
        ProcessUserAgent.Write(context.ActionDescriptor.DisplayName,
                               "SampleAsyncPageFilter.OnPageHandlerSelectionAsync",
                               value, key.ToString());

        return Task.CompletedTask;
    }

    public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context,
                                                  PageHandlerExecutionDelegate next)
    {
        // Do post work.
        await next.Invoke();
    }
}

W poprzednim kodzie jest dostarczany przez użytkownika kod, ProcessUserAgent.Write który współpracuje z ciągiem agenta użytkownika.

Poniższy kod włącza klasę SampleAsyncPageFilterStartup w klasie :

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.Filters.Add(new SampleAsyncPageFilter(Configuration));
        });
}

Następujące wywołania AddFolderApplicationModelConvention kodu, aby zastosować metodę SampleAsyncPageFilter do tylko stron w /Movies:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages(options =>
    {
        options.Conventions.AddFolderApplicationModelConvention(
            "/Movies",
            model => model.Filters.Add(new SampleAsyncPageFilter(Configuration)));
    });
}

Poniższy kod implementuje synchroniczną metodę IPageFilter:

public class SamplePageFilter : IPageFilter
{
    private readonly IConfiguration _config;

    public SamplePageFilter(IConfiguration config)
    {
        _config = config;
    }

    public void OnPageHandlerSelected(PageHandlerSelectedContext context)
    {
        var key = _config["UserAgentID"];
        context.HttpContext.Request.Headers.TryGetValue("user-agent", out StringValues value);
        ProcessUserAgent.Write(context.ActionDescriptor.DisplayName,
                               "SamplePageFilter.OnPageHandlerSelected",
                               value, key.ToString());
    }

    public void OnPageHandlerExecuting(PageHandlerExecutingContext context)
    {
        Debug.WriteLine("Global sync OnPageHandlerExecuting called.");
    }

    public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
    {
        Debug.WriteLine("Global sync OnPageHandlerExecuted called.");
    }
}

Następujący kod włącza funkcję SamplePageFilter:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.Filters.Add(new SamplePageFilter(Configuration));
        });
}

Implementowanie Razor filtrów stron przez zastępowanie metod filtrowania

Poniższy kod zastępuje filtry strony asynchronicznej Razor :

public class IndexModel : PageModel
{
    private readonly IConfiguration _config;

    public IndexModel(IConfiguration config)
    {
        _config = config;
    }

    public override Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
    {
        Debug.WriteLine("/IndexModel OnPageHandlerSelectionAsync");
        return Task.CompletedTask;
    }

    public async override Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, 
                                                           PageHandlerExecutionDelegate next)
    {
        var key = _config["UserAgentID"];
        context.HttpContext.Request.Headers.TryGetValue("user-agent", out StringValues value);
        ProcessUserAgent.Write(context.ActionDescriptor.DisplayName,
                               "/IndexModel-OnPageHandlerExecutionAsync",
                                value, key.ToString());

        await next.Invoke();
    }
}

Implementowanie atrybutu filtru

Wbudowany filtr filtru OnResultExecutionAsync oparty na atrybutach może być podklasowany. Następujący filtr dodaje nagłówek do odpowiedzi:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;

namespace PageFilter.Filters
{
    public class AddHeaderAttribute  : ResultFilterAttribute
    {
        private readonly string _name;
        private readonly string _value;

        public AddHeaderAttribute (string name, string value)
        {
            _name = name;
            _value = value;
        }

        public override void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(_name, new string[] { _value });
        }
    }
}

Następujący kod stosuje AddHeader atrybut:

using Microsoft.AspNetCore.Mvc.RazorPages;
using PageFilter.Filters;

namespace PageFilter.Movies
{
    [AddHeader("Author", "Rick")]
    public class TestModel : PageModel
    {
        public void OnGet()
        {

        }
    }
}

Użyj narzędzia, takiego jak narzędzia deweloperskie przeglądarki, aby zbadać nagłówki. W obszarze Nagłówkiauthor: Rick odpowiedzi zostanie wyświetlona wartość .

Aby uzyskać instrukcje dotyczące zastępowania kolejności, zobacz Zastępowanie kolejności domyślnej.

Aby uzyskać instrukcje dotyczące zwarć potoku filtru z filtru, zobacz Anulowanie i zwarcie .

Autoryzowanie atrybutu filtru

Atrybut Authorize można zastosować do elementu PageModel:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace PageFilter.Pages
{
    [Authorize]
    public class ModelWithAuthFilterModel : PageModel
    {
        public IActionResult OnGet() => Page();
    }
}

Autor: Rick Anderson

Razor Filtry IPageFilter stron i IAsyncPageFilter zezwalaj Razor na uruchamianie kodu przez strony przed uruchomieniem i po uruchomieniu Razor programu obsługi strony. Razor Filtry stron są podobne do filtrów akcji ASP.NET Core MVC, z wyjątkiem tego, że nie można ich stosować do poszczególnych metod obsługi stron.

Razor Filtry stron:

  • Uruchom kod po wybraniu metody obsługi, ale przed wystąpieniem powiązania modelu.
  • Uruchom kod przed wykonaniem metody obsługi po zakończeniu powiązania modelu.
  • Uruchom kod po wykonaniu metody obsługi.
  • Można zaimplementować na stronie lub globalnie.
  • Nie można zastosować do określonych metod obsługi stron.

Kod można uruchomić przed wykonaniem metody obsługi przy użyciu konstruktora strony lub oprogramowania pośredniczącego, ale tylko Razor filtry strony mają dostęp do HttpContextprogramu . Filtry mają FilterContext parametr pochodny, który zapewnia dostęp do HttpContextelementu . Na przykład przykład Implementowanie atrybutu filtru dodaje nagłówek do odpowiedzi, czego nie można zrobić z konstruktorami lub oprogramowaniem pośredniczącym.

Wyświetl lub pobierz przykładowy kod (jak pobrać)

Razor Filtry stron zapewniają następujące metody, które można zastosować globalnie lub na poziomie strony:

  • Metody synchroniczne:

  • Metody asynchroniczne:

Uwaga

Zaimplementuj synchroniczną lub asynchroniczną wersję interfejsu filtru, a nie obie. Struktura najpierw sprawdza, czy filtr implementuje interfejs asynchroniczny, a jeśli tak, wywołuje to. Jeśli nie, wywołuje metody interfejsu synchronicznego. Jeśli oba interfejsy są implementowane, wywoływane są tylko metody asynchroniczne. Ta sama reguła ma zastosowanie do przesłonięć na stronach, zaimplementować synchroniczną lub asynchroniczną wersję przesłonięcia, a nie obie.

Implementowanie Razor filtrów stron globalnie

Poniższy kod implementuje IAsyncPageFilterelement :

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace PageFilter.Filters
{
    public class SampleAsyncPageFilter : IAsyncPageFilter
    {
        private readonly ILogger _logger;

        public SampleAsyncPageFilter(ILogger logger)
        {
            _logger = logger;
        }

        public async Task OnPageHandlerSelectionAsync(
                                            PageHandlerSelectedContext context)
        {
            _logger.LogDebug("Global OnPageHandlerSelectionAsync called.");
            await Task.CompletedTask;
        }

        public async Task OnPageHandlerExecutionAsync(
                                            PageHandlerExecutingContext context,
                                            PageHandlerExecutionDelegate next)
        {
            _logger.LogDebug("Global OnPageHandlerExecutionAsync called.");
            await next.Invoke();
        }
    }
}

W poprzednim kodzie ILogger nie jest wymagany. Jest on używany w przykładzie do dostarczania informacji śledzenia dla aplikacji.

Poniższy kod włącza klasę SampleAsyncPageFilterStartup w klasie :

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Filters.Add(new SampleAsyncPageFilter(_logger));
    });
}

Poniższy kod przedstawia pełną Startup klasę:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using PageFilter.Filters;

namespace PageFilter
{
    public class Startup
    {
        ILogger _logger;
        public Startup(ILoggerFactory loggerFactory, IConfiguration configuration)
        {
            _logger = loggerFactory.CreateLogger<GlobalFiltersLogger>();
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(options =>
            {
                options.Filters.Add(new SampleAsyncPageFilter(_logger));
            });
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseMvc();
        }
    }
}

Następujące wywołania AddFolderApplicationModelConvention kodu, aby zastosować SampleAsyncPageFilter element do tylko stron w folderze /subFolder:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
       .AddRazorPagesOptions(options =>
       {
           options.Conventions.AddFolderApplicationModelConvention(
               "/subFolder",
               model => model.Filters.Add(new SampleAsyncPageFilter(_logger)));
       });
}

Poniższy kod implementuje synchroniczną metodę IPageFilter:

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;

namespace PageFilter.Filters
{
    public class SamplePageFilter : IPageFilter
    {
        private readonly ILogger _logger;

        public SamplePageFilter(ILogger logger)
        {
            _logger = logger;
        }

        public void OnPageHandlerSelected(PageHandlerSelectedContext context)
        {
            _logger.LogDebug("Global sync OnPageHandlerSelected called.");
        }

        public void OnPageHandlerExecuting(PageHandlerExecutingContext context)
        {
            _logger.LogDebug("Global sync PageHandlerExecutingContext called.");
        }

        public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
        {
            _logger.LogDebug("Global sync OnPageHandlerExecuted called.");
        }
    }
}

Następujący kod włącza funkcję SamplePageFilter:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Filters.Add(new SamplePageFilter(_logger));
    });
}

Implementowanie Razor filtrów stron przez zastępowanie metod filtrowania

Poniższy kod zastępuje synchroniczne Razor filtry strony:

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace PageFilter.Pages
{
    public class IndexModel : PageModel
    {
        private readonly ILogger _logger;

        public IndexModel(ILogger<IndexModel> logger)
        {
            _logger = logger;
        }
        public string Message { get; set; }

        public void OnGet()
        {
            _logger.LogDebug("IndexModel/OnGet");
        }
        
        public override void OnPageHandlerSelected(
                                    PageHandlerSelectedContext context)
        {
            _logger.LogDebug("IndexModel/OnPageHandlerSelected");          
        }

        public override void OnPageHandlerExecuting(
                                    PageHandlerExecutingContext context)
        {
            Message = "Message set in handler executing";
            _logger.LogDebug("IndexModel/OnPageHandlerExecuting");
        }


        public override void OnPageHandlerExecuted(
                                    PageHandlerExecutedContext context)
        {
            _logger.LogDebug("IndexModel/OnPageHandlerExecuted");
        }
    }
}

Implementowanie atrybutu filtru

Wbudowany filtr filtru OnResultExecutionAsync oparty na atrybutach może być podklasowany. Następujący filtr dodaje nagłówek do odpowiedzi:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;

namespace PageFilter.Filters
{
    public class AddHeaderAttribute  : ResultFilterAttribute
    {
        private readonly string _name;
        private readonly string _value;

        public AddHeaderAttribute (string name, string value)
        {
            _name = name;
            _value = value;
        }

        public override void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(_name, new string[] { _value });
        }
    }
}

Następujący kod stosuje AddHeader atrybut:

[AddHeader("Author", "Rick")]
public class ContactModel : PageModel
{
    private readonly ILogger _logger;

    public ContactModel(ILogger<ContactModel> logger)
    {
        _logger = logger;
    }
    public string Message { get; set; }

    public async Task OnGetAsync()
    {
        Message = "Your contact page.";
        _logger.LogDebug("Contact/OnGet");
        await Task.CompletedTask;
    }
}

Aby uzyskać instrukcje dotyczące zastępowania kolejności, zobacz Zastępowanie kolejności domyślnej.

Aby uzyskać instrukcje dotyczące zwarć potoku filtru z filtru, zobacz Anulowanie i zwarcie .

Autoryzowanie atrybutu filtru

Atrybut Authorize można zastosować do elementu PageModel:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace PageFilter.Pages
{
    [Authorize]
    public class ModelWithAuthFilterModel : PageModel
    {
        public IActionResult OnGet() => Page();
    }
}