Metodi di filtro per Razor Pages in ASP.NET Core

Di Rick Anderson

RazorIPageFilter Filtri di pagina e IAsyncPageFilter consentono alle pagine di Razor eseguire il codice prima e dopo l'esecuzione di un Razor gestore pagina. Razor I filtri di pagina sono simili ai filtri di azione core MVC ASP.NET, ad eccezione del fatto che non possono essere applicati ai singoli metodi del gestore di pagine.

Razor Filtri di pagina:

  • Eseguono il codice dopo aver selezionato un metodo del gestore, ma prima che si verifichi l'associazione di modelli.
  • Eseguono il codice prima che il metodo del gestore venga eseguito, al termine dell'associazione di modelli.
  • Eseguono il codice dopo l'esecuzione del metodo del gestore.
  • Possono essere implementati in una pagina o a livello globale.
  • Non possono essere applicati a metodi del gestore pagina specifici.
  • Può avere dipendenze del costruttore popolate da inserimento delle dipendenze.Can have constructor dependency dependencies populated by Dependency Injection (DI). Per altre informazioni, vedere ServiceFilterAttribute e TypeFilterAttribute.

Mentre i costruttori di pagine e il middleware consentono l'esecuzione di codice personalizzato prima dell'esecuzione di un metodo del gestore, solo Razor i filtri pagina consentono l'accesso a HttpContext e alla pagina. Il middleware ha accesso a HttpContext, ma non al "contesto della pagina". I filtri hanno un FilterContext parametro derivato, che fornisce l'accesso a HttpContext. Ecco un esempio per un filtro di pagina: implementare un attributo di filtro che aggiunge un'intestazione alla risposta, operazione che non può essere eseguita con costruttori o middleware. L'accesso al contesto della pagina, che include l'accesso alle istanze della pagina e al modello, è disponibile solo quando si eseguono filtri, gestori o corpo di una Razor pagina.

Visualizzare o scaricare il codice di esempio (procedura per il download)

Razor I filtri di pagina forniscono i metodi seguenti, che possono essere applicati a livello globale o a livello di pagina:

  • Metodi sincroni:

    • OnPageHandlerSelected : Chiamato dopo che è stato selezionato un metodo del gestore, ma prima dell'associazione di modelli.
    • OnPageHandlerExecuting : Chiamato prima dell'esecuzione del metodo del gestore, dopo il completamento dell'associazione di modelli.
    • OnPageHandlerExecuted : Chiamato dopo l'esecuzione del metodo del gestore, prima del risultato dell'azione.
  • Metodi asincroni:

    • OnPageHandlerSelectionAsync : Chiamato in modo asincrono dopo che il metodo del gestore è stato selezionato, ma prima dell'associazione di modelli.
    • OnPageHandlerExecutionAsync : Chiamato in modo asincrono prima che venga richiamato il metodo del gestore, dopo il completamento dell'associazione di modelli.

Implementare la versione sincrona oppure la versione asincrona di un'interfaccia di filtro, non entrambe. Il framework controlla per prima cosa se il filtro implementa l'interfaccia asincrona e, in tal caso, la chiama. In caso contrario, chiama i metodi dell'interfaccia sincrona. Se vengono implementate entrambe le interfacce, vengono chiamati solo i metodi asincroni. La stessa regola vale per gli override nelle pagine. Implementare la versione sincrona o la versione asincrona dell'override, non entrambe.

Implementare i Razor filtri di pagina a livello globale

Il codice seguente implementa IAsyncPageFilter:

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

Nel codice precedente, ProcessUserAgent.Write è il codice fornito dall'utente che funziona con la stringa dell'agente utente.

Il codice seguente abilita SampleAsyncPageFilter nella classe Startup:

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

Il codice seguente chiama AddFolderApplicationModelConvention per applicare a SampleAsyncPageFilter solo le pagine in /Movies:

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

Il codice seguente implementa il filtro IPageFilter sincrono:

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.");
    }
}

Il codice seguente abilita SamplePageFilter:

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

Implementare Razor filtri di pagina eseguendo l'override dei metodi di filtro

Il codice seguente esegue l'override dei filtri pagina asincroni 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();
    }
}

Implementare un attributo di filtro

Il filtro OnResultExecutionAsync basato su attributi predefinito può essere sottoclassato. Il filtro seguente aggiunge un'intestazione alla risposta:

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

Il codice seguente applica l'attributo AddHeader:

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

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

        }
    }
}

Usare uno strumento come gli strumenti di sviluppo del browser per esaminare le intestazioni. In Intestazioniauthor: Rick di risposta viene visualizzato.

Vedere Override dell'ordine predefinito per istruzioni su come eseguire l'override dell'ordine.

Vedere Annullamento e blocco per istruzioni su come bloccare la pipeline filtro da un filtro.

Autorizzare l'attributo di filtro

L'attributo Authorize può essere applicato a 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();
    }
}

Di Rick Anderson

RazorIPageFilter Filtri di pagina e IAsyncPageFilter consentono alle pagine di Razor eseguire il codice prima e dopo l'esecuzione di un Razor gestore pagina. Razor I filtri di pagina sono simili ai filtri di azione core MVC ASP.NET, ad eccezione del fatto che non possono essere applicati ai singoli metodi del gestore di pagine.

Razor Filtri di pagina:

  • Eseguono il codice dopo aver selezionato un metodo del gestore, ma prima che si verifichi l'associazione di modelli.
  • Eseguono il codice prima che il metodo del gestore venga eseguito, al termine dell'associazione di modelli.
  • Eseguono il codice dopo l'esecuzione del metodo del gestore.
  • Possono essere implementati in una pagina o a livello globale.
  • Non possono essere applicati a metodi del gestore pagina specifici.

Il codice può essere eseguito prima dell'esecuzione di un metodo del gestore usando il costruttore della pagina o il middleware, ma solo Razor i filtri Page hanno accesso a HttpContext. I filtri hanno un FilterContext parametro derivato, che fornisce l'accesso a HttpContext. Ad esempio, nell'esempio Implementare un attributo di filtro viene aggiunta un'intestazione alla risposta, operazione impossibile con costruttori o middleware.

Visualizzare o scaricare il codice di esempio (procedura per il download)

Razor I filtri di pagina forniscono i metodi seguenti, che possono essere applicati a livello globale o a livello di pagina:

  • Metodi sincroni:

    • OnPageHandlerSelected : Chiamato dopo che è stato selezionato un metodo del gestore, ma prima dell'associazione di modelli.
    • OnPageHandlerExecuting : Chiamato prima dell'esecuzione del metodo del gestore, dopo il completamento dell'associazione di modelli.
    • OnPageHandlerExecuted : Chiamato dopo l'esecuzione del metodo del gestore, prima del risultato dell'azione.
  • Metodi asincroni:

    • OnPageHandlerSelectionAsync : Chiamato in modo asincrono dopo che il metodo del gestore è stato selezionato, ma prima dell'associazione di modelli.
    • OnPageHandlerExecutionAsync : Chiamato in modo asincrono prima che venga richiamato il metodo del gestore, dopo il completamento dell'associazione di modelli.

Nota

Implementare la versione sincrona o la versione asincrona di un'interfaccia di filtro, non entrambe. Il framework controlla per prima cosa se il filtro implementa l'interfaccia asincrona e, in tal caso, la chiama. In caso contrario, chiama i metodi dell'interfaccia sincrona. Se vengono implementate entrambe le interfacce, vengono chiamati solo i metodi asincroni. La stessa regola vale per gli override nelle pagine. Implementare la versione sincrona o la versione asincrona dell'override, non entrambe.

Implementare i Razor filtri di pagina a livello globale

Il codice seguente implementa IAsyncPageFilter:

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

Nel codice ILogger precedente non è necessario. Nell'esempio viene utilizzato per fornire informazioni di traccia per l'applicazione.

Il codice seguente abilita SampleAsyncPageFilter nella classe Startup:

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

Il codice seguente mostra la classe Startup completa:

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

Il codice seguente chiama AddFolderApplicationModelConvention per applicare SampleAsyncPageFilter solo alle pagine in /subFolder:

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

Il codice seguente implementa il filtro IPageFilter sincrono:

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.");
        }
    }
}

Il codice seguente abilita SamplePageFilter:

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

Implementare Razor filtri di pagina eseguendo l'override dei metodi di filtro

Il codice seguente esegue l'override dei filtri pagina sincroni Razor :

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

Implementare un attributo di filtro

Il filtro OnResultExecutionAsync basato su attributi predefinito può essere sottoclassato. Il filtro seguente aggiunge un'intestazione alla risposta:

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

Il codice seguente applica l'attributo AddHeader:

[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;
    }
}

Vedere Override dell'ordine predefinito per istruzioni su come eseguire l'override dell'ordine.

Vedere Annullamento e blocco per istruzioni su come bloccare la pipeline filtro da un filtro.

Autorizzare l'attributo di filtro

L'attributo Authorize può essere applicato a 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();
    }
}