Metode filter untuk Razor Pages di ASP.NET Core

Oleh Rick Anderson

Razor Halaman memfilter IPageFilter dan IAsyncPageFilter mengizinkan Razor Halaman menjalankan kode sebelum dan sesudah Razor handler Halaman dijalankan. Razor Filter halaman mirip dengan filter tindakan MVC Inti ASP.NET, kecuali filter tidak dapat diterapkan ke metode penangan halaman individual.

Razor Filter halaman:

  • Jalankan kode setelah metode handler dipilih, tetapi sebelum pengikatan model terjadi.
  • Jalankan kode sebelum metode handler dijalankan, setelah pengikatan model selesai.
  • Jalankan kode setelah metode handler dijalankan.
  • Dapat diimplementasikan pada halaman atau secara global.
  • Tidak dapat diterapkan ke metode penangan halaman tertentu.
  • Dapat memiliki dependensi konstruktor yang diisi oleh Injeksi Dependensi (DI). Untuk informasi selengkapnya, lihat ServiceFilterAttribute dan TypeFilterAttribute.

Meskipun konstruktor halaman dan middleware memungkinkan eksekusi kode kustom sebelum metode handler dijalankan, hanya Razor filter Halaman yang memungkinkan akses ke HttpContext dan halaman. Middleware memiliki akses ke HttpContext, tetapi tidak ke "konteks halaman". Filter memiliki FilterContext parameter turunan, yang menyediakan akses ke HttpContext. Berikut adalah sampel untuk filter halaman: Terapkan atribut filter yang menambahkan header ke respons, sesuatu yang tidak dapat dilakukan dengan konstruktor atau middleware. Akses ke konteks halaman, yang mencakup akses ke instans halaman dan modelnya, hanya tersedia saat menjalankan filter, handler, atau isi Razor Halaman.

Melihat atau mengunduh kode sampel (cara mengunduh)

Razor Filter halaman menyediakan metode berikut, yang dapat diterapkan secara global atau di tingkat halaman:

  • Metode sinkron:

  • Metode asinkron:

Terapkan versi sinkron atau asinkron dari antarmuka filter, bukan keduanya. Kerangka kerja memeriksa terlebih dahulu untuk melihat apakah filter mengimplementasikan antarmuka asinkron, dan jika demikian, ia memanggilnya. Jika tidak, ini memanggil metode antarmuka sinkron. Jika kedua antarmuka diimplementasikan, hanya metode asinkron yang dipanggil. Aturan yang sama berlaku untuk mengambil alih di halaman, menerapkan versi sinkron atau asinkron dari penimpaan, bukan keduanya.

Menerapkan Razor filter Halaman secara global

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

Dalam kode sebelumnya, ProcessUserAgent.Write adalah kode yang disediakan pengguna yang berfungsi dengan string agen pengguna.

Kode berikut mengaktifkan SampleAsyncPageFilter di Startup kelas :

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

Kode berikut memanggil AddFolderApplicationModelConvention untuk menerapkan satu-satunya SampleAsyncPageFilter halaman di /Movies:

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

Kode berikut mengimplementasikan sinkron 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.");
    }
}

Kode berikut mengaktifkan SamplePageFilter:

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

Menerapkan Razor filter Halaman dengan menimpa metode filter

Kode berikut mengambil alih filter Halaman asinkron 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();
    }
}

Menerapkan atribut filter

Filter filter OnResultExecutionAsync berbasis atribut bawaan dapat disubkelas. Filter berikut menambahkan header ke respons:

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

Kode berikut menerapkan AddHeader atribut :

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

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

        }
    }
}

Gunakan alat seperti alat pengembang browser untuk memeriksa header. Di bawah Header Respons, author: Rick ditampilkan.

Lihat Mengesampingkan urutan default untuk instruksi tentang mengambil alih pesanan.

Lihat Pembatalan dan sirkuit pendek untuk instruksi untuk sirkuit pendek alur filter dari filter.

Mengotorisasi atribut filter

Atribut Otorisasi dapat diterapkan ke 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();
    }
}

Oleh Rick Anderson

Razor Halaman memfilter IPageFilter dan IAsyncPageFilter mengizinkan Razor Halaman menjalankan kode sebelum dan sesudah Razor handler Halaman dijalankan. Razor Filter halaman mirip dengan filter tindakan MVC Inti ASP.NET, kecuali filter tidak dapat diterapkan ke metode penangan halaman individual.

Razor Filter halaman:

  • Jalankan kode setelah metode handler dipilih, tetapi sebelum pengikatan model terjadi.
  • Jalankan kode sebelum metode handler dijalankan, setelah pengikatan model selesai.
  • Jalankan kode setelah metode handler dijalankan.
  • Dapat diimplementasikan pada halaman atau secara global.
  • Tidak dapat diterapkan ke metode penangan halaman tertentu.

Kode dapat dijalankan sebelum metode handler dijalankan menggunakan konstruktor halaman atau middleware, tetapi hanya Razor filter Halaman yang memiliki akses ke HttpContext. Filter memiliki FilterContext parameter turunan, yang menyediakan akses ke HttpContext. Misalnya, sampel Terapkan atribut filter menambahkan header ke respons, sesuatu yang tidak dapat dilakukan dengan konstruktor atau middleware.

Melihat atau mengunduh kode sampel (cara mengunduh)

Razor Filter halaman menyediakan metode berikut, yang dapat diterapkan secara global atau di tingkat halaman:

  • Metode sinkron:

  • Metode asinkron:

Catatan

Terapkan versi sinkron atau asinkron dari antarmuka filter, bukan keduanya. Kerangka kerja memeriksa terlebih dahulu untuk melihat apakah filter mengimplementasikan antarmuka asinkron, dan jika demikian, ia memanggilnya. Jika tidak, ini memanggil metode antarmuka sinkron. Jika kedua antarmuka diimplementasikan, hanya metode asinkron yang dipanggil. Aturan yang sama berlaku untuk mengambil alih di halaman, menerapkan versi sinkron atau asinkron dari penimpaan, bukan keduanya.

Menerapkan Razor filter Halaman secara global

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

Dalam kode sebelumnya, ILogger tidak diperlukan. Ini digunakan dalam sampel untuk memberikan informasi pelacakan untuk aplikasi.

Kode berikut mengaktifkan SampleAsyncPageFilter di Startup kelas :

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

Kode berikut menunjukkan kelas lengkap Startup :

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

Kode berikut memanggil AddFolderApplicationModelConvention untuk menerapkan satu-satunya SampleAsyncPageFilter halaman di /subFolder:

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

Kode berikut mengimplementasikan sinkron 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.");
        }
    }
}

Kode berikut mengaktifkan SamplePageFilter:

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

Menerapkan Razor filter Halaman dengan menimpa metode filter

Kode berikut mengesampingkan filter Halaman sinkron 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");
        }
    }
}

Menerapkan atribut filter

Filter filter OnResultExecutionAsync berbasis atribut bawaan dapat disubkelas. Filter berikut menambahkan header ke respons:

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

Kode berikut menerapkan AddHeader atribut :

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

Lihat Mengesampingkan urutan default untuk instruksi tentang mengambil alih pesanan.

Lihat Pembatalan dan sirkuit pendek untuk instruksi untuk sirkuit pendek alur filter dari filter.

Mengotorisasi atribut filter

Atribut Otorisasi dapat diterapkan ke 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();
    }
}