Bagikan melalui


Filter di ASP.NET Core

Oleh Kirk Larkin, Rick Anderson, Tom Dykstra, dan Steve Smith

Filter di ASP.NET Core memungkinkan kode dijalankan sebelum atau sesudah tahap tertentu dalam alur pemrosesan permintaan.

Filter bawaan menangani tugas seperti:

  • Otorisasi, mencegah akses ke sumber daya yang tidak diotorisasi pengguna.
  • Penembolokan respons, sirkuit pendek alur permintaan untuk mengembalikan respons cache.

Filter kustom dapat dibuat untuk menangani masalah pemotongan silang. Contoh masalah lintas pemotongan termasuk penanganan kesalahan, penembolokan, konfigurasi, otorisasi, dan pengelogan. Filter menghindari kode duplikat. Misalnya, filter pengecualian penanganan kesalahan dapat mengonsolidasikan penanganan kesalahan.

Dokumen ini berlaku untuk Razor Halaman, pengontrol API, dan pengontrol dengan tampilan. Filter tidak berfungsi langsung dengan Razor komponen. Filter hanya dapat secara tidak langsung memengaruhi komponen saat:

  • Komponen disematkan dalam halaman atau tampilan.
  • Halaman atau pengontrol dan tampilan menggunakan filter.

Cara kerja filter

Filter berjalan dalam alur pemanggilan tindakan ASP.NET Core, kadang-kadang disebut sebagai alur filter. Alur filter berjalan setelah ASP.NET Core memilih tindakan yang akan dijalankan:

The request is processed through Other Middleware, Routing Middleware, Action Selection, and the Action Invocation Pipeline. The request processing continues back through Action Selection, Routing Middleware, and various Other Middleware before becoming a response sent to the client.

Jenis filter

Setiap jenis filter dijalankan pada tahap yang berbeda dalam alur filter:

  • Filter otorisasi:

    • Jalankan terlebih dahulu.
    • Tentukan apakah pengguna berwenang untuk permintaan tersebut.
    • Sirkuit pendek alur jika permintaan tidak diotorisasi.
  • Filter sumber daya:

    • Jalankan setelah otorisasi.
    • OnResourceExecuting menjalankan kode sebelum alur filter lainnya. Misalnya, OnResourceExecuting menjalankan kode sebelum pengikatan model.
    • OnResourceExecuted menjalankan kode setelah alur lainnya selesai.
  • Filter tindakan:

    • Jalankan segera sebelum dan sesudah metode tindakan dipanggil.
    • Dapat mengubah argumen yang diteruskan ke dalam tindakan.
    • Dapat mengubah hasil yang dikembalikan dari tindakan.
    • Tidak didukung di Razor Halaman.
  • Filter titik akhir:

    • Jalankan segera sebelum dan sesudah metode tindakan dipanggil.
    • Dapat mengubah argumen yang diteruskan ke dalam tindakan.
    • Dapat mengubah hasil yang dikembalikan dari tindakan.
    • Tidak didukung di Razor Halaman.
    • Dapat dipanggil pada tindakan dan titik akhir berbasis handler rute.
  • Filter pengecualian menerapkan kebijakan global ke pengecualian yang tidak tertangani yang terjadi sebelum isi respons ditulis.

  • Filter hasil:

    • Jalankan segera sebelum dan sesudah eksekusi hasil tindakan.
    • Jalankan hanya ketika metode tindakan berhasil dijalankan.
    • Berguna untuk logika yang harus mengelilingi tampilan atau eksekusi formatter.

Diagram berikut menunjukkan bagaimana jenis filter berinteraksi dalam alur filter:

The request is processed through Authorization Filters, Resource Filters, Model Binding, Action Filters, Action Execution and Action Result Conversion, Exception Filters, Result Filters, and Result Execution. On the way out, the request is only processed by Result Filters and Resource Filters before becoming a response sent to the client.

Razor Pages juga mendukung Razor filter Halaman, yang berjalan sebelum dan sesudah Razor handler Halaman.

implementasi

Filter mendukung implementasi sinkron dan asinkron melalui definisi antarmuka yang berbeda.

Filter sinkron berjalan sebelum dan sesudah tahap alurnya. Misalnya, OnActionExecuting dipanggil sebelum metode tindakan dipanggil. OnActionExecuted dipanggil setelah metode tindakan mengembalikan:

public class SampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

Filter asinkron mendefinisikan On-Stage-ExecutionAsync metode. Misalnya, OnActionExecutionAsync:

public class SampleAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // Do something before the action executes.
        await next();
        // Do something after the action executes.
    }
}

Dalam kode sebelumnya, SampleAsyncActionFilter memiliki ActionExecutionDelegate, , nextyang menjalankan metode tindakan.

Beberapa tahap filter

Antarmuka untuk beberapa tahap filter dapat diimplementasikan dalam satu kelas. Misalnya, ActionFilterAttribute kelas mengimplementasikan:

Terapkan versi sinkron atau asinkron dari antarmuka filter, bukan keduanya. Runtime memeriksa terlebih dahulu untuk melihat apakah filter mengimplementasikan antarmuka asinkron, dan jika demikian, filter memanggilnya. Jika tidak, ini memanggil metode antarmuka sinkron. Jika antarmuka asinkron dan sinkron diimplementasikan dalam satu kelas, hanya metode asinkron yang dipanggil. Saat menggunakan kelas abstrak seperti ActionFilterAttribute, hanya ambil alih metode sinkron atau metode asinkron untuk setiap jenis filter.

Atribut filter bawaan

ASP.NET Core mencakup filter berbasis atribut bawaan yang dapat disubkelas dan disesuaikan. Misalnya, filter hasil berikut menambahkan header ke respons:

public class ResponseHeaderAttribute : ActionFilterAttribute
{
    private readonly string _name;
    private readonly string _value;

    public ResponseHeaderAttribute(string name, string value) =>
        (_name, _value) = (name, value);

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, _value);

        base.OnResultExecuting(context);
    }
}

Atribut memungkinkan filter untuk menerima argumen, seperti yang ditunjukkan dalam contoh sebelumnya. Terapkan ke ResponseHeaderAttribute pengontrol atau metode tindakan dan tentukan nama dan nilai header HTTP:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
    public IActionResult Index() =>
        Content("Examine the response headers using the F12 developer tools.");

    // ...

Gunakan alat seperti alat pengembang browser untuk memeriksa header. Di bawah Header Respons, filter-header: Filter Value ditampilkan.

Kode berikut berlaku ResponseHeaderAttribute untuk pengontrol dan tindakan:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
    public IActionResult Index() =>
        Content("Examine the response headers using the F12 developer tools.");

    // ...

    [ResponseHeader("Another-Filter-Header", "Another Filter Value")]
    public IActionResult Multiple() =>
        Content("Examine the response headers using the F12 developer tools.");
}

Respons dari Multiple tindakan mencakup header berikut:

  • filter-header: Filter Value
  • another-filter-header: Another Filter Value

Beberapa antarmuka filter memiliki atribut yang sesuai yang dapat digunakan sebagai kelas dasar untuk implementasi kustom.

Atribut filter:

Filter tidak dapat diterapkan ke Razor metode handler Halaman. Mereka dapat diterapkan baik ke Razor model Halaman atau secara global.

Memfilter cakupan dan urutan eksekusi

Filter dapat ditambahkan ke alur di salah satu dari tiga cakupan:

  • Menggunakan atribut pada pengontrol atau Razor Halaman.
  • Menggunakan atribut pada tindakan pengontrol. Atribut filter tidak dapat diterapkan ke Razor metode handler Pages.
  • Secara global untuk semua pengontrol, tindakan, dan Razor Halaman seperti yang ditunjukkan dalam kode berikut:
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.AddControllersWithViews(options =>
    {
        options.Filters.Add<GlobalSampleActionFilter>();
    });
    

Urutan eksekusi default

Ketika ada beberapa filter untuk tahap tertentu dari alur, cakupan menentukan urutan default eksekusi filter. Filter global mengelilingi filter kelas, yang pada gilirannya mengelilingi filter metode.

Sebagai hasil dari filter bersarang, kode setelah filter berjalan dalam urutan terbalik dari kode sebelumnya . Urutan filter:

  • Kode sebelum filter global.
    • Kode sebelum filter pengontrol.
      • Kode sebelum filter metode tindakan.
      • Kode setelah filter metode tindakan.
    • Kode setelah filter pengontrol.
  • Kode setelah filter global.

Contoh berikut mengilustrasikan urutan metode filter yang dijalankan untuk filter tindakan sinkron:

Sequence Cakupan filter Metode filter
1 Global OnActionExecuting
2 Pengontrol OnActionExecuting
3 Tindakan OnActionExecuting
4 Tindakan OnActionExecuted
5 Pengontrol OnActionExecuted
6 Global OnActionExecuted

Filter tingkat pengontrol

Setiap pengontrol yang mewarisi dari Controller mencakup OnActionExecutingmetode , , OnActionExecutionAsyncdan OnActionExecuted . Metode ini membungkus filter yang berjalan untuk tindakan tertentu:

  • OnActionExecuting berjalan sebelum salah satu filter tindakan.
  • OnActionExecuted berjalan setelah semua filter tindakan.
  • OnActionExecutionAsync berjalan sebelum salah satu filter tindakan. Kode setelah panggilan untuk next berjalan setelah filter tindakan.

Kelas berikut ControllerFiltersController :

  • SampleActionFilterAttribute Menerapkan ([SampleActionFilter]) ke pengontrol.
  • Mengambil alih OnActionExecuting dan OnActionExecuted.
[SampleActionFilter]
public class ControllerFiltersController : Controller
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuting)}");

        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuted)}");

        base.OnActionExecuted(context);
    }

    public IActionResult Index()
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(Index)}");

        return Content("Check the Console.");
    }
}

Menavigasi untuk https://localhost:<port>/ControllerFilters menjalankan kode berikut:

  • ControllerFiltersController.OnActionExecuting
    • GlobalSampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • ControllerFiltersController.Index
      • SampleActionFilterAttribute.OnActionExecuted
    • GlobalSampleActionFilter.OnActionExecuted
  • ControllerFiltersController.OnActionExecuted

Filter tingkat pengontrol mengatur properti Pesanan ke int.MinValue. Filter tingkat pengontrol tidak dapat diatur untuk dijalankan setelah filter diterapkan ke metode. Pesanan dijelaskan di bagian berikutnya.

Untuk Razor Halaman, lihat Menerapkan Razor filter Halaman dengan menimpa metode filter.

Mengambil alih urutan default

Urutan default eksekusi dapat diganti dengan menerapkan IOrderedFilter. IOrderedFilterOrder mengekspos properti yang lebih diutamakan daripada cakupan untuk menentukan urutan eksekusi. Filter dengan nilai yang lebih rendah Order :

  • Menjalankan kode sebelum filter dengan nilai yang lebih tinggi dari Order.
  • Menjalankan kode setelah itu dari filter dengan nilai yang lebih tinggiOrder.

Dalam contoh filter tingkat Pengontrol, GlobalSampleActionFilter memiliki cakupan global sehingga berjalan sebelum SampleActionFilterAttribute, yang memiliki cakupan pengontrol. Untuk menjalankan SampleActionFilterAttribute terlebih dahulu, atur urutannya ke int.MinValue:

[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
    // ...
}

Untuk membuat filter GlobalSampleActionFilter global berjalan terlebih dahulu, atur Order ke int.MinValue:

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});

Pembatalan dan sirkuit pendek

Alur filter dapat disusun pendek dengan mengatur Result properti pada parameter yang ResourceExecutingContext disediakan untuk metode filter. Misalnya, filter Sumber Daya berikut mencegah sisa alur dieksekusi:

public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        context.Result = new ContentResult
        {
            Content = nameof(ShortCircuitingResourceFilterAttribute)
        };
    }

    public void OnResourceExecuted(ResourceExecutedContext context) { }
}

Dalam kode berikut, dan [ShortCircuitingResourceFilter][ResponseHeader] filter menargetkan Index metode tindakan. Filter ShortCircuitingResourceFilterAttribute :

  • Berjalan terlebih dahulu, karena ini adalah Filter Sumber Daya dan ResponseHeaderAttribute merupakan Filter Tindakan.
  • Sirkuit pendek sisa alur.

ResponseHeaderAttribute Oleh karena itu filter tidak pernah berjalan untuk tindakan.Index Perilaku ini akan sama jika kedua filter diterapkan pada tingkat metode tindakan, asalkan ShortCircuitingResourceFilterAttribute dijalankan terlebih dahulu. ShortCircuitingResourceFilterAttribute Eksekusi pertama karena jenis filternya:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
    [ShortCircuitingResourceFilter]
    public IActionResult Index() =>
        Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}

Injeksi dependensi

Filter dapat ditambahkan berdasarkan jenis atau berdasarkan instans. Jika instans ditambahkan, instans tersebut digunakan untuk setiap permintaan. Jika jenis ditambahkan, jenis diaktifkan. Filter yang diaktifkan jenis berarti:

  • Instans dibuat untuk setiap permintaan.
  • Setiap dependensi konstruktor diisi oleh injeksi dependensi (DI).

Filter yang diimplementasikan sebagai atribut dan ditambahkan langsung ke kelas pengontrol atau metode tindakan tidak dapat memiliki dependensi konstruktor yang disediakan oleh injeksi dependensi (DI). Dependensi konstruktor tidak dapat disediakan oleh DI karena atribut harus memiliki parameter konstruktor yang disediakan di mana mereka diterapkan.

Filter berikut mendukung dependensi konstruktor yang disediakan dari DI:

Filter sebelumnya dapat diterapkan ke pengontrol atau tindakan.

Pencatat tersedia dari DI. Namun, hindari membuat dan menggunakan filter murni untuk tujuan pengelogan. Pengelogan kerangka kerja bawaan biasanya menyediakan apa yang diperlukan untuk pengelogan. Pengelogan ditambahkan ke filter:

  • Harus fokus pada masalah atau perilaku domain bisnis khusus untuk filter.
  • Tidak boleh mencatat tindakan atau peristiwa kerangka kerja lainnya. Filter bawaan sudah mencatat tindakan dan peristiwa kerangka kerja.

ServiceFilterAttribute

Jenis implementasi filter layanan terdaftar di Program.cs. Mengambil ServiceFilterAttribute instans filter dari DI.

Kode berikut menunjukkan LoggingResponseHeaderFilterService kelas , yang menggunakan DI:

public class LoggingResponseHeaderFilterService : IResultFilter
{
    private readonly ILogger _logger;

    public LoggingResponseHeaderFilterService(
            ILogger<LoggingResponseHeaderFilterService> logger) =>
        _logger = logger;

    public void OnResultExecuting(ResultExecutingContext context)
    {
        _logger.LogInformation(
            $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}");

        context.HttpContext.Response.Headers.Add(
            nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService));
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        _logger.LogInformation(
            $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}");
    }
}

Dalam kode berikut, LoggingResponseHeaderFilterService ditambahkan ke kontainer DI:

builder.Services.AddScoped<LoggingResponseHeaderFilterService>();

Dalam kode berikut, ServiceFilter atribut mengambil instans LoggingResponseHeaderFilterService filter dari DI:

[ServiceFilter<LoggingResponseHeaderFilterService>]
public IActionResult WithServiceFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");

Saat menggunakan ServiceFilterAttribute, pengaturan ServiceFilterAttribute.IsReusable:

  • Memberikan petunjuk bahwa instans filter dapat digunakan kembali di luar cakupan permintaan tempat instans dibuat. Runtime ASP.NET Core tidak menjamin:
    • Bahwa satu instans filter akan dibuat.
    • Filter tidak akan diminta kembali dari kontainer DI di beberapa titik kemudian.
  • Tidak boleh digunakan dengan filter yang bergantung pada layanan dengan masa pakai selain singleton.

ServiceFilterAttribute penerapan IFilterFactory. IFilterFactorymengekspos metode untuk membuat IFilterMetadata instansCreateInstance. CreateInstance memuat jenis yang ditentukan dari DI.

TypeFilterAttribute

TypeFilterAttribute mirip ServiceFilterAttributedengan , tetapi jenisnya tidak diselesaikan langsung dari kontainer DI. Ini membuat instans jenis dengan menggunakan Microsoft.Extensions.DependencyInjection.ObjectFactory.

Karena TypeFilterAttribute jenis tidak diselesaikan langsung dari kontainer DI:

  • Jenis yang direferensikan menggunakan TypeFilterAttribute tidak perlu didaftarkan dengan kontainer DI. Mereka memang memiliki dependensi yang dipenuhi oleh kontainer DI.
  • TypeFilterAttribute dapat secara opsional menerima argumen konstruktor untuk jenis tersebut.

Saat menggunakan TypeFilterAttribute, pengaturan TypeFilterAttribute.IsReusable:

  • Memberikan petunjuk bahwa instans filter dapat digunakan kembali di luar cakupan permintaan tempat instans dibuat. Runtime ASP.NET Core tidak memberikan jaminan bahwa satu instans filter akan dibuat.

  • Tidak boleh digunakan dengan filter yang bergantung pada layanan dengan masa pakai selain singleton.

Contoh berikut menunjukkan cara meneruskan argumen ke jenis menggunakan TypeFilterAttribute:

[TypeFilter(typeof(LoggingResponseHeaderFilter),
    Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");

Filter otorisasi

Filter otorisasi:

  • Apakah filter pertama dijalankan di alur filter.
  • Mengontrol akses ke metode tindakan.
  • Memiliki metode sebelumnya, tetapi tidak setelah metode.

Filter otorisasi kustom memerlukan kerangka kerja otorisasi kustom. Lebih suka mengonfigurasi kebijakan otorisasi atau menulis kebijakan otorisasi kustom daripada menulis filter kustom. Filter otorisasi bawaan:

  • Memanggil sistem otorisasi.
  • Tidak mengotorisasi permintaan.

Jangan melemparkan pengecualian dalam filter otorisasi:

  • Pengecualian tidak akan ditangani.
  • Filter pengecualian tidak akan menangani pengecualian.

Pertimbangkan untuk mengeluarkan tantangan saat pengecualian terjadi di filter otorisasi.

Pelajari selengkapnya tentang Otorisasi.

Filter sumber daya

Filter sumber daya:

Filter sumber daya berguna untuk sirkuit pendek sebagian besar alur. Misalnya, filter penembolokan dapat menghindari sisa alur pada hit cache.

Contoh filter sumber daya:

  • Filter sumber daya sirkuit pendek yang ditunjukkan sebelumnya.

  • DisableFormValueModelBindingAttribute:

    • Mencegah pengikatan model mengakses data formulir.
    • Digunakan untuk unggahan file besar untuk mencegah data formulir dibaca ke dalam memori.

Filter tindakan

Filter tindakan tidak berlaku untuk Razor Halaman. Razor Pages mendukung IPageFilter dan IAsyncPageFilter. Untuk informasi selengkapnya, lihat Metode filter untuk Razor Halaman.

Filter tindakan:

Kode berikut menunjukkan filter tindakan sampel:

public class SampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

menyediakan ActionExecutingContext properti berikut:

  • ActionArguments - memungkinkan membaca input ke metode tindakan.
  • Controller - memungkinkan memanipulasi instans pengontrol.
  • Result - mengatur Result eksekusi sirkuit pendek dari metode tindakan dan filter tindakan berikutnya.

Melemparkan pengecualian dalam metode tindakan:

  • Mencegah berjalannya filter berikutnya.
  • Tidak seperti pengaturan Result, diperlakukan sebagai kegagalan alih-alih hasil yang berhasil.

menyediakan ActionExecutedContextController dan Result ditambah properti berikut:

  • Canceled - Benar jika eksekusi tindakan disusun pendek oleh filter lain.
  • Exception - Non-null jika tindakan atau filter tindakan yang dijalankan sebelumnya melemparkan pengecualian. Mengatur properti ini ke null:
    • Menangani pengecualian secara efektif.
    • Result dijalankan seolah-olah dikembalikan dari metode tindakan.

IAsyncActionFilterUntuk , panggilan ke ActionExecutionDelegate:

  • Menjalankan filter tindakan berikutnya dan metode tindakan.
  • Menampilkan ActionExecutedContext.

Untuk sirkuit pendek, tetapkan Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result ke instans hasil dan jangan panggil next ().ActionExecutionDelegate

Kerangka kerja menyediakan abstrak ActionFilterAttribute yang dapat disubkelas.

OnActionExecuting Filter tindakan dapat digunakan untuk:

  • Memvalidasi status model.
  • Mengembalikan kesalahan jika status tidak valid.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}

Catatan

Pengontrol yang dianotasi dengan [ApiController] atribut secara otomatis memvalidasi status model dan mengembalikan respons 400. Untuk informasi selengkapnya, lihat Respons HTTP 400 otomatis.

Metode OnActionExecuted berjalan setelah metode tindakan:

  • Dan dapat melihat dan memanipulasi hasil tindakan melalui Result properti .
  • Canceled diatur ke true jika eksekusi tindakan disusun pendek oleh filter lain.
  • Exception diatur ke nilai non-null jika tindakan atau filter tindakan berikutnya melemparkan pengecualian. Pengaturan Exception ke null:
    • Menangani pengecualian secara efektif.
    • ActionExecutedContext.Result dijalankan seolah-olah dikembalikan secara normal dari metode tindakan.

Filter pengecualian

Filter pengecualian:

Filter pengecualian sampel berikut menampilkan detail tentang pengecualian yang terjadi saat aplikasi sedang dalam pengembangan:

public class SampleExceptionFilter : IExceptionFilter
{
    private readonly IHostEnvironment _hostEnvironment;

    public SampleExceptionFilter(IHostEnvironment hostEnvironment) =>
        _hostEnvironment = hostEnvironment;

    public void OnException(ExceptionContext context)
    {
        if (!_hostEnvironment.IsDevelopment())
        {
            // Don't display exception details unless running in Development.
            return;
        }

        context.Result = new ContentResult
        {
            Content = context.Exception.ToString()
        };
    }
}

Kode berikut menguji filter pengecualian:

[TypeFilter<SampleExceptionFilter>]
public class ExceptionController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}

Filter pengecualian:

  • Tidak memiliki peristiwa sebelum dan sesudah.
  • Terapkan OnException atau OnExceptionAsync.
  • Tangani pengecualian yang tidak tertangani yang terjadi di Razor Pembuatan halaman atau pengontrol, pengikatan model, filter tindakan, atau metode tindakan.
  • Jangan menangkap pengecualian yang terjadi dalam filter sumber daya, filter hasil, atau eksekusi hasil MVC.

Untuk menangani pengecualian, atur properti ke ExceptionHandledtrue atau tetapkan Result properti . Ini menghentikan penyebaran pengecualian. Filter pengecualian tidak dapat mengubah pengecualian menjadi "sukses". Hanya filter tindakan yang dapat melakukannya.

Filter pengecualian:

  • Baik untuk menjebak pengecualian yang terjadi dalam tindakan.
  • Tidak fleksibel seperti middleware penanganan kesalahan.

Lebih suka middleware untuk penanganan pengecualian. Gunakan filter pengecualian hanya di mana penanganan kesalahan berbeda berdasarkan metode tindakan mana yang dipanggil. Misalnya, aplikasi mungkin memiliki metode tindakan untuk titik akhir API dan untuk tampilan/HTML. Titik akhir API dapat mengembalikan informasi kesalahan sebagai JSAKTIF, sementara tindakan berbasis tampilan dapat mengembalikan halaman kesalahan sebagai HTML.

Filter hasil

Filter hasil:

IResultFilter dan IAsyncResultFilter

Kode berikut menunjukkan contoh filter hasil:

public class SampleResultFilter : IResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        // Do something before the result executes.
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Do something after the result executes.
    }
}

Jenis hasil yang dijalankan tergantung pada tindakan. Tindakan yang mengembalikan tampilan mencakup semua pemrosesan pisau cukur sebagai bagian dari yang ViewResult dijalankan. Metode API mungkin melakukan beberapa serialisasi sebagai bagian dari eksekusi hasilnya. Pelajari selengkapnya tentang hasil tindakan.

Filter hasil hanya dijalankan saat filter tindakan atau tindakan menghasilkan hasil tindakan. Filter hasil tidak dijalankan ketika:

  • Filter otorisasi atau filter sumber daya sirkuit pendek alur.
  • Filter pengecualian menangani pengecualian dengan menghasilkan hasil tindakan.

Metode ini Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting dapat melakukan eksekusi sirkuit pendek dari hasil tindakan dan filter hasil berikutnya dengan mengatur Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel ke true. Tulis ke objek respons saat sirkuit pendek untuk menghindari menghasilkan respons kosong. Melemparkan pengecualian di IResultFilter.OnResultExecuting:

  • Mencegah eksekusi hasil tindakan dan filter berikutnya.
  • Diperlakukan sebagai kegagalan alih-alih hasil yang berhasil.

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted Ketika metode berjalan, respons mungkin sudah dikirim ke klien. Jika respons telah dikirim ke klien, respons tidak dapat diubah.

ResultExecutedContext.Canceled diatur ke true jika eksekusi hasil tindakan disusun pendek oleh filter lain.

ResultExecutedContext.Exception diatur ke nilai non-null jika hasil tindakan atau filter hasil berikutnya melemparkan pengecualian. Pengaturan Exception ke null secara efektif menangani pengecualian dan mencegah pengecualian dilemparkan lagi nanti di alur. Tidak ada cara yang dapat diandalkan untuk menulis data ke respons saat menangani pengecualian dalam filter hasil. Jika header telah dibersihkan ke klien ketika hasil tindakan melemparkan pengecualian, tidak ada mekanisme yang dapat diandalkan untuk mengirim kode kegagalan.

IAsyncResultFilterUntuk , panggilan ke await nextResultExecutionDelegate pada menjalankan filter hasil berikutnya dan hasil tindakan. Untuk sirkuit pendek, atur ResultExecutingContext.Cancel ke true dan jangan panggil ResultExecutionDelegate:

public class SampleAsyncResultFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(
        ResultExecutingContext context, ResultExecutionDelegate next)
    {
        if (context.Result is not EmptyResult)
        {
            await next();
        }
        else
        {
            context.Cancel = true;
        }
    }
}

Kerangka kerja menyediakan abstrak ResultFilterAttribute yang dapat disubkelas. Kelas ResponseHeaderAttribute yang ditampilkan sebelumnya adalah contoh atribut filter hasil.

IAlwaysRunResultFilter dan IAsyncAlwaysRunResultFilter

Antarmuka IAlwaysRunResultFilter dan IAsyncAlwaysRunResultFilter mendeklarasikan IResultFilter implementasi yang berjalan untuk semua hasil tindakan. Ini termasuk hasil tindakan yang dihasilkan oleh:

  • Filter otorisasi dan filter sumber daya yang memiliki sirkuit pendek.
  • Filter pengecualian.

Misalnya, filter berikut selalu menjalankan dan menetapkan hasil tindakan (ObjectResult) dengan kode status Entitas Tidak Dapat Diproses 422 saat negosiasi konten gagal:

public class UnprocessableResultFilter : IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is StatusCodeResult statusCodeResult
            && statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType)
        {
            context.Result = new ObjectResult("Unprocessable")
            {
                StatusCode = StatusCodes.Status422UnprocessableEntity
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context) { }
}

IFilterFactory

IFilterFactory penerapan IFilterMetadata. Oleh karena itu, IFilterFactory instans dapat digunakan sebagai IFilterMetadata instans di mana saja dalam alur filter. Ketika runtime bersiap untuk memanggil filter, runtime mencoba untuk melemparkannya ke IFilterFactory. Jika transmisi tersebut berhasil, CreateInstance metode dipanggil untuk membuat IFilterMetadata instans yang dipanggil. Ini menyediakan desain yang fleksibel, karena alur filter yang tepat tidak perlu diatur secara eksplisit saat aplikasi dimulai.

IFilterFactory.IsReusable:

  • Adalah petunjuk dari pabrik bahwa instans filter yang dibuat oleh pabrik dapat digunakan kembali di luar cakupan permintaan tempat instans dibuat.
  • Tidak boleh digunakan dengan filter yang bergantung pada layanan dengan masa pakai selain singleton.

Runtime ASP.NET Core tidak menjamin:

  • Bahwa satu instans filter akan dibuat.
  • Filter tidak akan diminta kembali dari kontainer DI di beberapa titik kemudian.

Peringatan

Hanya konfigurasikan IFilterFactory.IsReusable untuk mengembalikan true jika sumber filter tidak ambigu, filter tidak bersifat stateless, dan filter aman digunakan di beberapa permintaan HTTP. Misalnya, jangan mengembalikan filter dari DI yang terdaftar sebagai tercakup atau sementara jika IFilterFactory.IsReusable mengembalikan true.

IFilterFactory dapat diimplementasikan menggunakan implementasi atribut kustom sebagai pendekatan lain untuk membuat filter:

public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
        new InternalResponseHeaderFilter();

    private class InternalResponseHeaderFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context) =>
            context.HttpContext.Response.Headers.Add(
                nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));

        public void OnActionExecuted(ActionExecutedContext context) { }
    }

Filter diterapkan dalam kode berikut:

[ResponseHeaderFilterFactory]
public IActionResult Index() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");

IFilterFactory diimplementasikan pada atribut

Filter yang diterapkan IFilterFactory berguna untuk filter yang:

  • Tidak memerlukan parameter passing.
  • Memiliki dependensi konstruktor yang perlu diisi oleh DI.

TypeFilterAttribute penerapan IFilterFactory. IFilterFactorymengekspos metode untuk membuat IFilterMetadata instansCreateInstance. CreateInstance memuat jenis yang ditentukan dari kontainer layanan (DI).

public class SampleActionTypeFilterAttribute : TypeFilterAttribute
{
    public SampleActionTypeFilterAttribute()
         : base(typeof(InternalSampleActionFilter)) { }

    private class InternalSampleActionFilter : IActionFilter
    {
        private readonly ILogger<InternalSampleActionFilter> _logger;

        public InternalSampleActionFilter(ILogger<InternalSampleActionFilter> logger) =>
            _logger = logger;

        public void OnActionExecuting(ActionExecutingContext context)
        {
            _logger.LogInformation(
                $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuting)}");
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _logger.LogInformation(
                $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuted)}");
        }
    }
}

Kode berikut menunjukkan tiga pendekatan untuk menerapkan filter:

[SampleActionTypeFilter]
public IActionResult WithDirectAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}");

[TypeFilter<SampleActionTypeFilterAttribute>]
public IActionResult WithTypeFilterAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}");

[ServiceFilter<SampleActionTypeFilterAttribute>]
public IActionResult WithServiceFilterAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}");

Dalam kode sebelumnya, pendekatan pertama untuk menerapkan filter lebih disukai.

Menggunakan middleware dalam alur filter

Filter sumber daya berfungsi seperti middleware karena mereka mengelilingi eksekusi semua yang muncul nanti dalam alur. Tetapi filter berbeda dari middleware karena mereka adalah bagian dari runtime, yang berarti bahwa mereka memiliki akses ke konteks dan konstruksi.

Untuk menggunakan middleware sebagai filter, buat jenis dengan Configure metode yang menentukan middleware untuk dimasukkan ke dalam alur filter. Contoh berikut menggunakan middleware untuk mengatur header respons:

public class FilterMiddlewarePipeline
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            context.Response.Headers.Add("Pipeline", "Middleware");

            await next();
        });
    }
}

MiddlewareFilterAttribute Gunakan untuk menjalankan middleware:

[MiddlewareFilter<FilterMiddlewarePipeline>]
public class FilterMiddlewareController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}

Filter middleware berjalan pada tahap alur filter yang sama dengan filter Sumber Daya, sebelum pengikatan model dan setelah alur lainnya.

Keamanan utas

Saat meneruskan instans filter ke , Addalih-alih Type, filternya adalah singleton dan tidak aman untuk utas.

Sumber Daya Tambahan:

Oleh Kirk Larkin, Rick Anderson, Tom Dykstra, dan Steve Smith

Filter di ASP.NET Core memungkinkan kode dijalankan sebelum atau sesudah tahap tertentu dalam alur pemrosesan permintaan.

Filter bawaan menangani tugas seperti:

  • Otorisasi, mencegah akses ke sumber daya yang tidak diotorisasi pengguna.
  • Penembolokan respons, sirkuit pendek alur permintaan untuk mengembalikan respons cache.

Filter kustom dapat dibuat untuk menangani masalah pemotongan silang. Contoh masalah lintas pemotongan termasuk penanganan kesalahan, penembolokan, konfigurasi, otorisasi, dan pengelogan. Filter menghindari kode duplikat. Misalnya, filter pengecualian penanganan kesalahan dapat mengonsolidasikan penanganan kesalahan.

Dokumen ini berlaku untuk Razor Halaman, pengontrol API, dan pengontrol dengan tampilan. Filter tidak berfungsi langsung dengan Razor komponen. Filter hanya dapat secara tidak langsung memengaruhi komponen saat:

  • Komponen disematkan dalam halaman atau tampilan.
  • Halaman atau pengontrol dan tampilan menggunakan filter.

Cara kerja filter

Filter berjalan dalam alur pemanggilan tindakan ASP.NET Core, kadang-kadang disebut sebagai alur filter. Alur filter berjalan setelah ASP.NET Core memilih tindakan yang akan dijalankan:

The request is processed through Other Middleware, Routing Middleware, Action Selection, and the Action Invocation Pipeline. The request processing continues back through Action Selection, Routing Middleware, and various Other Middleware before becoming a response sent to the client.

Jenis filter

Setiap jenis filter dijalankan pada tahap yang berbeda dalam alur filter:

  • Filter otorisasi:

    • Jalankan terlebih dahulu.
    • Tentukan apakah pengguna berwenang untuk permintaan tersebut.
    • Sirkuit pendek alur jika permintaan tidak diotorisasi.
  • Filter sumber daya:

    • Jalankan setelah otorisasi.
    • OnResourceExecuting menjalankan kode sebelum alur filter lainnya. Misalnya, OnResourceExecuting menjalankan kode sebelum pengikatan model.
    • OnResourceExecuted menjalankan kode setelah alur lainnya selesai.
  • Filter tindakan:

    • Jalankan segera sebelum dan sesudah metode tindakan dipanggil.
    • Dapat mengubah argumen yang diteruskan ke dalam tindakan.
    • Dapat mengubah hasil yang dikembalikan dari tindakan.
    • Tidak didukung di Razor Halaman.
  • Filter titik akhir:

    • Jalankan segera sebelum dan sesudah metode tindakan dipanggil.
    • Dapat mengubah argumen yang diteruskan ke dalam tindakan.
    • Dapat mengubah hasil yang dikembalikan dari tindakan.
    • Tidak didukung di Razor Halaman.
    • Dapat dipanggil pada tindakan dan titik akhir berbasis handler rute.
  • Filter pengecualian menerapkan kebijakan global ke pengecualian yang tidak tertangani yang terjadi sebelum isi respons ditulis.

  • Filter hasil:

    • Jalankan segera sebelum dan sesudah eksekusi hasil tindakan.
    • Jalankan hanya ketika metode tindakan berhasil dijalankan.
    • Berguna untuk logika yang harus mengelilingi tampilan atau eksekusi formatter.

Diagram berikut menunjukkan bagaimana jenis filter berinteraksi dalam alur filter:

The request is processed through Authorization Filters, Resource Filters, Model Binding, Action Filters, Action Execution and Action Result Conversion, Exception Filters, Result Filters, and Result Execution. On the way out, the request is only processed by Result Filters and Resource Filters before becoming a response sent to the client.

Razor Pages juga mendukung Razor filter Halaman, yang berjalan sebelum dan sesudah Razor handler Halaman.

implementasi

Filter mendukung implementasi sinkron dan asinkron melalui definisi antarmuka yang berbeda.

Filter sinkron berjalan sebelum dan sesudah tahap alurnya. Misalnya, OnActionExecuting dipanggil sebelum metode tindakan dipanggil. OnActionExecuted dipanggil setelah metode tindakan mengembalikan:

public class SampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

Filter asinkron mendefinisikan On-Stage-ExecutionAsync metode. Misalnya, OnActionExecutionAsync:

public class SampleAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // Do something before the action executes.
        await next();
        // Do something after the action executes.
    }
}

Dalam kode sebelumnya, SampleAsyncActionFilter memiliki ActionExecutionDelegate, , nextyang menjalankan metode tindakan.

Beberapa tahap filter

Antarmuka untuk beberapa tahap filter dapat diimplementasikan dalam satu kelas. Misalnya, ActionFilterAttribute kelas mengimplementasikan:

Terapkan versi sinkron atau asinkron dari antarmuka filter, bukan keduanya. Runtime memeriksa terlebih dahulu untuk melihat apakah filter mengimplementasikan antarmuka asinkron, dan jika demikian, filter memanggilnya. Jika tidak, ini memanggil metode antarmuka sinkron. Jika antarmuka asinkron dan sinkron diimplementasikan dalam satu kelas, hanya metode asinkron yang dipanggil. Saat menggunakan kelas abstrak seperti ActionFilterAttribute, hanya ambil alih metode sinkron atau metode asinkron untuk setiap jenis filter.

Atribut filter bawaan

ASP.NET Core mencakup filter berbasis atribut bawaan yang dapat disubkelas dan disesuaikan. Misalnya, filter hasil berikut menambahkan header ke respons:

public class ResponseHeaderAttribute : ActionFilterAttribute
{
    private readonly string _name;
    private readonly string _value;

    public ResponseHeaderAttribute(string name, string value) =>
        (_name, _value) = (name, value);

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, _value);

        base.OnResultExecuting(context);
    }
}

Atribut memungkinkan filter untuk menerima argumen, seperti yang ditunjukkan dalam contoh sebelumnya. Terapkan ke ResponseHeaderAttribute pengontrol atau metode tindakan dan tentukan nama dan nilai header HTTP:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
    public IActionResult Index() =>
        Content("Examine the response headers using the F12 developer tools.");

    // ...

Gunakan alat seperti alat pengembang browser untuk memeriksa header. Di bawah Header Respons, filter-header: Filter Value ditampilkan.

Kode berikut berlaku ResponseHeaderAttribute untuk pengontrol dan tindakan:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
    public IActionResult Index() =>
        Content("Examine the response headers using the F12 developer tools.");

    // ...

    [ResponseHeader("Another-Filter-Header", "Another Filter Value")]
    public IActionResult Multiple() =>
        Content("Examine the response headers using the F12 developer tools.");
}

Respons dari Multiple tindakan mencakup header berikut:

  • filter-header: Filter Value
  • another-filter-header: Another Filter Value

Beberapa antarmuka filter memiliki atribut yang sesuai yang dapat digunakan sebagai kelas dasar untuk implementasi kustom.

Atribut filter:

Filter tidak dapat diterapkan ke Razor metode handler Halaman. Mereka dapat diterapkan baik ke Razor model Halaman atau secara global.

Memfilter cakupan dan urutan eksekusi

Filter dapat ditambahkan ke alur di salah satu dari tiga cakupan:

  • Menggunakan atribut pada pengontrol atau Razor Halaman.
  • Menggunakan atribut pada tindakan pengontrol. Atribut filter tidak dapat diterapkan ke Razor metode handler Pages.
  • Secara global untuk semua pengontrol, tindakan, dan Razor Halaman seperti yang ditunjukkan dalam kode berikut:
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.AddControllersWithViews(options =>
    {
        options.Filters.Add<GlobalSampleActionFilter>();
    });
    

Urutan eksekusi default

Ketika ada beberapa filter untuk tahap tertentu dari alur, cakupan menentukan urutan default eksekusi filter. Filter global mengelilingi filter kelas, yang pada gilirannya mengelilingi filter metode.

Sebagai hasil dari filter bersarang, kode setelah filter berjalan dalam urutan terbalik dari kode sebelumnya . Urutan filter:

  • Kode sebelum filter global.
    • Kode sebelum filter pengontrol.
      • Kode sebelum filter metode tindakan.
      • Kode setelah filter metode tindakan.
    • Kode setelah filter pengontrol.
  • Kode setelah filter global.

Contoh berikut mengilustrasikan urutan metode filter yang dijalankan untuk filter tindakan sinkron:

Sequence Cakupan filter Metode filter
1 Global OnActionExecuting
2 Pengontrol OnActionExecuting
3 Tindakan OnActionExecuting
4 Tindakan OnActionExecuted
5 Pengontrol OnActionExecuted
6 Global OnActionExecuted

Filter tingkat pengontrol

Setiap pengontrol yang mewarisi dari Controller mencakup OnActionExecutingmetode , , OnActionExecutionAsyncdan OnActionExecuted . Metode ini membungkus filter yang berjalan untuk tindakan tertentu:

  • OnActionExecuting berjalan sebelum salah satu filter tindakan.
  • OnActionExecuted berjalan setelah semua filter tindakan.
  • OnActionExecutionAsync berjalan sebelum salah satu filter tindakan. Kode setelah panggilan untuk next berjalan setelah filter tindakan.

Kelas berikut ControllerFiltersController :

  • SampleActionFilterAttribute Menerapkan ([SampleActionFilter]) ke pengontrol.
  • Mengambil alih OnActionExecuting dan OnActionExecuted.
[SampleActionFilter]
public class ControllerFiltersController : Controller
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuting)}");

        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuted)}");

        base.OnActionExecuted(context);
    }

    public IActionResult Index()
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(Index)}");

        return Content("Check the Console.");
    }
}

Menavigasi untuk https://localhost:<port>/ControllerFilters menjalankan kode berikut:

  • ControllerFiltersController.OnActionExecuting
    • GlobalSampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • ControllerFiltersController.Index
      • SampleActionFilterAttribute.OnActionExecuted
    • GlobalSampleActionFilter.OnActionExecuted
  • ControllerFiltersController.OnActionExecuted

Filter tingkat pengontrol mengatur properti Pesanan ke int.MinValue. Filter tingkat pengontrol tidak dapat diatur untuk dijalankan setelah filter diterapkan ke metode. Pesanan dijelaskan di bagian berikutnya.

Untuk Razor Halaman, lihat Menerapkan Razor filter Halaman dengan menimpa metode filter.

Mengambil alih urutan default

Urutan default eksekusi dapat diganti dengan menerapkan IOrderedFilter. IOrderedFilterOrder mengekspos properti yang lebih diutamakan daripada cakupan untuk menentukan urutan eksekusi. Filter dengan nilai yang lebih rendah Order :

  • Menjalankan kode sebelum filter dengan nilai yang lebih tinggi dari Order.
  • Menjalankan kode setelah itu dari filter dengan nilai yang lebih tinggiOrder.

Dalam contoh filter tingkat Pengontrol, GlobalSampleActionFilter memiliki cakupan global sehingga berjalan sebelum SampleActionFilterAttribute, yang memiliki cakupan pengontrol. Untuk menjalankan SampleActionFilterAttribute terlebih dahulu, atur urutannya ke int.MinValue:

[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
    // ...
}

Untuk membuat filter GlobalSampleActionFilter global berjalan terlebih dahulu, atur Order ke int.MinValue:

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});

Pembatalan dan sirkuit pendek

Alur filter dapat disusun pendek dengan mengatur Result properti pada parameter yang ResourceExecutingContext disediakan untuk metode filter. Misalnya, filter Sumber Daya berikut mencegah sisa alur dieksekusi:

public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        context.Result = new ContentResult
        {
            Content = nameof(ShortCircuitingResourceFilterAttribute)
        };
    }

    public void OnResourceExecuted(ResourceExecutedContext context) { }
}

Dalam kode berikut, dan [ShortCircuitingResourceFilter][ResponseHeader] filter menargetkan Index metode tindakan. Filter ShortCircuitingResourceFilterAttribute :

  • Berjalan terlebih dahulu, karena ini adalah Filter Sumber Daya dan ResponseHeaderAttribute merupakan Filter Tindakan.
  • Sirkuit pendek sisa alur.

ResponseHeaderAttribute Oleh karena itu filter tidak pernah berjalan untuk tindakan.Index Perilaku ini akan sama jika kedua filter diterapkan pada tingkat metode tindakan, asalkan ShortCircuitingResourceFilterAttribute dijalankan terlebih dahulu. ShortCircuitingResourceFilterAttribute Eksekusi pertama karena jenis filternya:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
    [ShortCircuitingResourceFilter]
    public IActionResult Index() =>
        Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}

Injeksi dependensi

Filter dapat ditambahkan berdasarkan jenis atau berdasarkan instans. Jika instans ditambahkan, instans tersebut digunakan untuk setiap permintaan. Jika jenis ditambahkan, jenis diaktifkan. Filter yang diaktifkan jenis berarti:

  • Instans dibuat untuk setiap permintaan.
  • Setiap dependensi konstruktor diisi oleh injeksi dependensi (DI).

Filter yang diimplementasikan sebagai atribut dan ditambahkan langsung ke kelas pengontrol atau metode tindakan tidak dapat memiliki dependensi konstruktor yang disediakan oleh injeksi dependensi (DI). Dependensi konstruktor tidak dapat disediakan oleh DI karena atribut harus memiliki parameter konstruktor yang disediakan di mana mereka diterapkan.

Filter berikut mendukung dependensi konstruktor yang disediakan dari DI:

Filter sebelumnya dapat diterapkan ke pengontrol atau tindakan.

Pencatat tersedia dari DI. Namun, hindari membuat dan menggunakan filter murni untuk tujuan pengelogan. Pengelogan kerangka kerja bawaan biasanya menyediakan apa yang diperlukan untuk pengelogan. Pengelogan ditambahkan ke filter:

  • Harus fokus pada masalah atau perilaku domain bisnis khusus untuk filter.
  • Tidak boleh mencatat tindakan atau peristiwa kerangka kerja lainnya. Filter bawaan sudah mencatat tindakan dan peristiwa kerangka kerja.

ServiceFilterAttribute

Jenis implementasi filter layanan terdaftar di Program.cs. Mengambil ServiceFilterAttribute instans filter dari DI.

Kode berikut menunjukkan LoggingResponseHeaderFilterService kelas , yang menggunakan DI:

public class LoggingResponseHeaderFilterService : IResultFilter
{
    private readonly ILogger _logger;

    public LoggingResponseHeaderFilterService(
            ILogger<LoggingResponseHeaderFilterService> logger) =>
        _logger = logger;

    public void OnResultExecuting(ResultExecutingContext context)
    {
        _logger.LogInformation(
            $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}");

        context.HttpContext.Response.Headers.Add(
            nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService));
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        _logger.LogInformation(
            $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}");
    }
}

Dalam kode berikut, LoggingResponseHeaderFilterService ditambahkan ke kontainer DI:

builder.Services.AddScoped<LoggingResponseHeaderFilterService>();

Dalam kode berikut, ServiceFilter atribut mengambil instans LoggingResponseHeaderFilterService filter dari DI:

[ServiceFilter(typeof(LoggingResponseHeaderFilterService))]
public IActionResult WithServiceFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");

Saat menggunakan ServiceFilterAttribute, pengaturan ServiceFilterAttribute.IsReusable:

  • Memberikan petunjuk bahwa instans filter dapat digunakan kembali di luar cakupan permintaan tempat instans dibuat. Runtime ASP.NET Core tidak menjamin:
    • Bahwa satu instans filter akan dibuat.
    • Filter tidak akan diminta kembali dari kontainer DI di beberapa titik kemudian.
  • Tidak boleh digunakan dengan filter yang bergantung pada layanan dengan masa pakai selain singleton.

ServiceFilterAttribute penerapan IFilterFactory. IFilterFactorymengekspos metode untuk membuat IFilterMetadata instansCreateInstance. CreateInstance memuat jenis yang ditentukan dari DI.

TypeFilterAttribute

TypeFilterAttribute mirip ServiceFilterAttributedengan , tetapi jenisnya tidak diselesaikan langsung dari kontainer DI. Ini membuat instans jenis dengan menggunakan Microsoft.Extensions.DependencyInjection.ObjectFactory.

Karena TypeFilterAttribute jenis tidak diselesaikan langsung dari kontainer DI:

  • Jenis yang direferensikan menggunakan TypeFilterAttribute tidak perlu didaftarkan dengan kontainer DI. Mereka memang memiliki dependensi yang dipenuhi oleh kontainer DI.
  • TypeFilterAttribute dapat secara opsional menerima argumen konstruktor untuk jenis tersebut.

Saat menggunakan TypeFilterAttribute, pengaturan TypeFilterAttribute.IsReusable:

  • Memberikan petunjuk bahwa instans filter dapat digunakan kembali di luar cakupan permintaan tempat instans dibuat. Runtime ASP.NET Core tidak memberikan jaminan bahwa satu instans filter akan dibuat.

  • Tidak boleh digunakan dengan filter yang bergantung pada layanan dengan masa pakai selain singleton.

Contoh berikut menunjukkan cara meneruskan argumen ke jenis menggunakan TypeFilterAttribute:

[TypeFilter(typeof(LoggingResponseHeaderFilter),
    Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");

Filter otorisasi

Filter otorisasi:

  • Apakah filter pertama dijalankan di alur filter.
  • Mengontrol akses ke metode tindakan.
  • Memiliki metode sebelumnya, tetapi tidak setelah metode.

Filter otorisasi kustom memerlukan kerangka kerja otorisasi kustom. Lebih suka mengonfigurasi kebijakan otorisasi atau menulis kebijakan otorisasi kustom daripada menulis filter kustom. Filter otorisasi bawaan:

  • Memanggil sistem otorisasi.
  • Tidak mengotorisasi permintaan.

Jangan melemparkan pengecualian dalam filter otorisasi:

  • Pengecualian tidak akan ditangani.
  • Filter pengecualian tidak akan menangani pengecualian.

Pertimbangkan untuk mengeluarkan tantangan saat pengecualian terjadi di filter otorisasi.

Pelajari selengkapnya tentang Otorisasi.

Filter sumber daya

Filter sumber daya:

Filter sumber daya berguna untuk sirkuit pendek sebagian besar alur. Misalnya, filter penembolokan dapat menghindari sisa alur pada hit cache.

Contoh filter sumber daya:

  • Filter sumber daya sirkuit pendek yang ditunjukkan sebelumnya.

  • DisableFormValueModelBindingAttribute:

    • Mencegah pengikatan model mengakses data formulir.
    • Digunakan untuk unggahan file besar untuk mencegah data formulir dibaca ke dalam memori.

Filter tindakan

Filter tindakan tidak berlaku untuk Razor Halaman. Razor Pages mendukung IPageFilter dan IAsyncPageFilter. Untuk informasi selengkapnya, lihat Metode filter untuk Razor Halaman.

Filter tindakan:

Kode berikut menunjukkan filter tindakan sampel:

public class SampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

menyediakan ActionExecutingContext properti berikut:

  • ActionArguments - memungkinkan membaca input ke metode tindakan.
  • Controller - memungkinkan memanipulasi instans pengontrol.
  • Result - mengatur Result eksekusi sirkuit pendek dari metode tindakan dan filter tindakan berikutnya.

Melemparkan pengecualian dalam metode tindakan:

  • Mencegah berjalannya filter berikutnya.
  • Tidak seperti pengaturan Result, diperlakukan sebagai kegagalan alih-alih hasil yang berhasil.

menyediakan ActionExecutedContextController dan Result ditambah properti berikut:

  • Canceled - Benar jika eksekusi tindakan disusun pendek oleh filter lain.
  • Exception - Non-null jika tindakan atau filter tindakan yang dijalankan sebelumnya melemparkan pengecualian. Mengatur properti ini ke null:
    • Menangani pengecualian secara efektif.
    • Result dijalankan seolah-olah dikembalikan dari metode tindakan.

IAsyncActionFilterUntuk , panggilan ke ActionExecutionDelegate:

  • Menjalankan filter tindakan berikutnya dan metode tindakan.
  • Menampilkan ActionExecutedContext.

Untuk sirkuit pendek, tetapkan Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result ke instans hasil dan jangan panggil next ().ActionExecutionDelegate

Kerangka kerja menyediakan abstrak ActionFilterAttribute yang dapat disubkelas.

OnActionExecuting Filter tindakan dapat digunakan untuk:

  • Memvalidasi status model.
  • Mengembalikan kesalahan jika status tidak valid.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}

Catatan

Pengontrol yang dianotasi dengan [ApiController] atribut secara otomatis memvalidasi status model dan mengembalikan respons 400. Untuk informasi selengkapnya, lihat Respons HTTP 400 otomatis.

Metode OnActionExecuted berjalan setelah metode tindakan:

  • Dan dapat melihat dan memanipulasi hasil tindakan melalui Result properti .
  • Canceled diatur ke true jika eksekusi tindakan disusun pendek oleh filter lain.
  • Exception diatur ke nilai non-null jika tindakan atau filter tindakan berikutnya melemparkan pengecualian. Pengaturan Exception ke null:
    • Menangani pengecualian secara efektif.
    • ActionExecutedContext.Result dijalankan seolah-olah dikembalikan secara normal dari metode tindakan.

Filter pengecualian

Filter pengecualian:

Filter pengecualian sampel berikut menampilkan detail tentang pengecualian yang terjadi saat aplikasi sedang dalam pengembangan:

public class SampleExceptionFilter : IExceptionFilter
{
    private readonly IHostEnvironment _hostEnvironment;

    public SampleExceptionFilter(IHostEnvironment hostEnvironment) =>
        _hostEnvironment = hostEnvironment;

    public void OnException(ExceptionContext context)
    {
        if (!_hostEnvironment.IsDevelopment())
        {
            // Don't display exception details unless running in Development.
            return;
        }

        context.Result = new ContentResult
        {
            Content = context.Exception.ToString()
        };
    }
}

Kode berikut menguji filter pengecualian:

[TypeFilter(typeof(SampleExceptionFilter))]
public class ExceptionController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}

Filter pengecualian:

  • Tidak memiliki peristiwa sebelum dan sesudah.
  • Terapkan OnException atau OnExceptionAsync.
  • Tangani pengecualian yang tidak tertangani yang terjadi di Razor Pembuatan halaman atau pengontrol, pengikatan model, filter tindakan, atau metode tindakan.
  • Jangan menangkap pengecualian yang terjadi dalam filter sumber daya, filter hasil, atau eksekusi hasil MVC.

Untuk menangani pengecualian, atur properti ke ExceptionHandledtrue atau tetapkan Result properti . Ini menghentikan penyebaran pengecualian. Filter pengecualian tidak dapat mengubah pengecualian menjadi "sukses". Hanya filter tindakan yang dapat melakukannya.

Filter pengecualian:

  • Baik untuk menjebak pengecualian yang terjadi dalam tindakan.
  • Tidak fleksibel seperti middleware penanganan kesalahan.

Lebih suka middleware untuk penanganan pengecualian. Gunakan filter pengecualian hanya di mana penanganan kesalahan berbeda berdasarkan metode tindakan mana yang dipanggil. Misalnya, aplikasi mungkin memiliki metode tindakan untuk titik akhir API dan untuk tampilan/HTML. Titik akhir API dapat mengembalikan informasi kesalahan sebagai JSAKTIF, sementara tindakan berbasis tampilan dapat mengembalikan halaman kesalahan sebagai HTML.

Filter hasil

Filter hasil:

IResultFilter dan IAsyncResultFilter

Kode berikut menunjukkan contoh filter hasil:

public class SampleResultFilter : IResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        // Do something before the result executes.
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Do something after the result executes.
    }
}

Jenis hasil yang dijalankan tergantung pada tindakan. Tindakan yang mengembalikan tampilan mencakup semua pemrosesan pisau cukur sebagai bagian dari yang ViewResult dijalankan. Metode API mungkin melakukan beberapa serialisasi sebagai bagian dari eksekusi hasilnya. Pelajari selengkapnya tentang hasil tindakan.

Filter hasil hanya dijalankan saat filter tindakan atau tindakan menghasilkan hasil tindakan. Filter hasil tidak dijalankan ketika:

  • Filter otorisasi atau filter sumber daya sirkuit pendek alur.
  • Filter pengecualian menangani pengecualian dengan menghasilkan hasil tindakan.

Metode ini Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting dapat melakukan eksekusi sirkuit pendek dari hasil tindakan dan filter hasil berikutnya dengan mengatur Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel ke true. Tulis ke objek respons saat sirkuit pendek untuk menghindari menghasilkan respons kosong. Melemparkan pengecualian di IResultFilter.OnResultExecuting:

  • Mencegah eksekusi hasil tindakan dan filter berikutnya.
  • Diperlakukan sebagai kegagalan alih-alih hasil yang berhasil.

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted Ketika metode berjalan, respons mungkin sudah dikirim ke klien. Jika respons telah dikirim ke klien, respons tidak dapat diubah.

ResultExecutedContext.Canceled diatur ke true jika eksekusi hasil tindakan disusun pendek oleh filter lain.

ResultExecutedContext.Exception diatur ke nilai non-null jika hasil tindakan atau filter hasil berikutnya melemparkan pengecualian. Pengaturan Exception ke null secara efektif menangani pengecualian dan mencegah pengecualian dilemparkan lagi nanti di alur. Tidak ada cara yang dapat diandalkan untuk menulis data ke respons saat menangani pengecualian dalam filter hasil. Jika header telah dibersihkan ke klien ketika hasil tindakan melemparkan pengecualian, tidak ada mekanisme yang dapat diandalkan untuk mengirim kode kegagalan.

IAsyncResultFilterUntuk , panggilan ke await nextResultExecutionDelegate pada menjalankan filter hasil berikutnya dan hasil tindakan. Untuk sirkuit pendek, atur ResultExecutingContext.Cancel ke true dan jangan panggil ResultExecutionDelegate:

public class SampleAsyncResultFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(
        ResultExecutingContext context, ResultExecutionDelegate next)
    {
        if (context.Result is not EmptyResult)
        {
            await next();
        }
        else
        {
            context.Cancel = true;
        }
    }
}

Kerangka kerja menyediakan abstrak ResultFilterAttribute yang dapat disubkelas. Kelas ResponseHeaderAttribute yang ditampilkan sebelumnya adalah contoh atribut filter hasil.

IAlwaysRunResultFilter dan IAsyncAlwaysRunResultFilter

Antarmuka IAlwaysRunResultFilter dan IAsyncAlwaysRunResultFilter mendeklarasikan IResultFilter implementasi yang berjalan untuk semua hasil tindakan. Ini termasuk hasil tindakan yang dihasilkan oleh:

  • Filter otorisasi dan filter sumber daya yang memiliki sirkuit pendek.
  • Filter pengecualian.

Misalnya, filter berikut selalu menjalankan dan menetapkan hasil tindakan (ObjectResult) dengan kode status Entitas Tidak Dapat Diproses 422 saat negosiasi konten gagal:

public class UnprocessableResultFilter : IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is StatusCodeResult statusCodeResult
            && statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType)
        {
            context.Result = new ObjectResult("Unprocessable")
            {
                StatusCode = StatusCodes.Status422UnprocessableEntity
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context) { }
}

IFilterFactory

IFilterFactory penerapan IFilterMetadata. Oleh karena itu, IFilterFactory instans dapat digunakan sebagai IFilterMetadata instans di mana saja dalam alur filter. Ketika runtime bersiap untuk memanggil filter, runtime mencoba untuk melemparkannya ke IFilterFactory. Jika transmisi tersebut berhasil, CreateInstance metode dipanggil untuk membuat IFilterMetadata instans yang dipanggil. Ini menyediakan desain yang fleksibel, karena alur filter yang tepat tidak perlu diatur secara eksplisit saat aplikasi dimulai.

IFilterFactory.IsReusable:

  • Adalah petunjuk dari pabrik bahwa instans filter yang dibuat oleh pabrik dapat digunakan kembali di luar cakupan permintaan tempat instans dibuat.
  • Tidak boleh digunakan dengan filter yang bergantung pada layanan dengan masa pakai selain singleton.

Runtime ASP.NET Core tidak menjamin:

  • Bahwa satu instans filter akan dibuat.
  • Filter tidak akan diminta kembali dari kontainer DI di beberapa titik kemudian.

Peringatan

Hanya konfigurasikan IFilterFactory.IsReusable untuk mengembalikan true jika sumber filter tidak ambigu, filter tidak bersifat stateless, dan filter aman digunakan di beberapa permintaan HTTP. Misalnya, jangan mengembalikan filter dari DI yang terdaftar sebagai tercakup atau sementara jika IFilterFactory.IsReusable mengembalikan true.

IFilterFactory dapat diimplementasikan menggunakan implementasi atribut kustom sebagai pendekatan lain untuk membuat filter:

public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
        new InternalResponseHeaderFilter();

    private class InternalResponseHeaderFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context) =>
            context.HttpContext.Response.Headers.Add(
                nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));

        public void OnActionExecuted(ActionExecutedContext context) { }
    }

Filter diterapkan dalam kode berikut:

[ResponseHeaderFilterFactory]
public IActionResult Index() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");

IFilterFactory diimplementasikan pada atribut

Filter yang diterapkan IFilterFactory berguna untuk filter yang:

  • Tidak memerlukan parameter passing.
  • Memiliki dependensi konstruktor yang perlu diisi oleh DI.

TypeFilterAttribute penerapan IFilterFactory. IFilterFactorymengekspos metode untuk membuat IFilterMetadata instansCreateInstance. CreateInstance memuat jenis yang ditentukan dari kontainer layanan (DI).

public class SampleActionTypeFilterAttribute : TypeFilterAttribute
{
    public SampleActionTypeFilterAttribute()
         : base(typeof(InternalSampleActionFilter)) { }

    private class InternalSampleActionFilter : IActionFilter
    {
        private readonly ILogger<InternalSampleActionFilter> _logger;

        public InternalSampleActionFilter(ILogger<InternalSampleActionFilter> logger) =>
            _logger = logger;

        public void OnActionExecuting(ActionExecutingContext context)
        {
            _logger.LogInformation(
                $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuting)}");
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _logger.LogInformation(
                $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuted)}");
        }
    }
}

Kode berikut menunjukkan tiga pendekatan untuk menerapkan filter:

[SampleActionTypeFilter]
public IActionResult WithDirectAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}");

[TypeFilter(typeof(SampleActionTypeFilterAttribute))]
public IActionResult WithTypeFilterAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}");

[ServiceFilter(typeof(SampleActionTypeFilterAttribute))]
public IActionResult WithServiceFilterAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}");

Dalam kode sebelumnya, pendekatan pertama untuk menerapkan filter lebih disukai.

Menggunakan middleware dalam alur filter

Filter sumber daya berfungsi seperti middleware karena mereka mengelilingi eksekusi semua yang muncul nanti dalam alur. Tetapi filter berbeda dari middleware karena mereka adalah bagian dari runtime, yang berarti bahwa mereka memiliki akses ke konteks dan konstruksi.

Untuk menggunakan middleware sebagai filter, buat jenis dengan Configure metode yang menentukan middleware untuk dimasukkan ke dalam alur filter. Contoh berikut menggunakan middleware untuk mengatur header respons:

public class FilterMiddlewarePipeline
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            context.Response.Headers.Add("Pipeline", "Middleware");

            await next();
        });
    }
}

MiddlewareFilterAttribute Gunakan untuk menjalankan middleware:

[MiddlewareFilter(typeof(FilterMiddlewarePipeline))]
public class FilterMiddlewareController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}

Filter middleware berjalan pada tahap alur filter yang sama dengan filter Sumber Daya, sebelum pengikatan model dan setelah alur lainnya.

Keamanan utas

Saat meneruskan instans filter ke , Addalih-alih Type, filternya adalah singleton dan tidak aman untuk utas.

Sumber Daya Tambahan:

Oleh Kirk Larkin, Rick Anderson, Tom Dykstra, dan Steve Smith

Filter di ASP.NET Core memungkinkan kode dijalankan sebelum atau sesudah tahap tertentu dalam alur pemrosesan permintaan.

Filter bawaan menangani tugas seperti:

  • Otorisasi, mencegah akses ke sumber daya yang tidak diotorisasi pengguna.
  • Penembolokan respons, sirkuit pendek alur permintaan untuk mengembalikan respons cache.

Filter kustom dapat dibuat untuk menangani masalah pemotongan silang. Contoh masalah lintas pemotongan termasuk penanganan kesalahan, penembolokan, konfigurasi, otorisasi, dan pengelogan. Filter menghindari kode duplikat. Misalnya, filter pengecualian penanganan kesalahan dapat mengonsolidasikan penanganan kesalahan.

Dokumen ini berlaku untuk Razor Halaman, pengontrol API, dan pengontrol dengan tampilan. Filter tidak berfungsi langsung dengan Razor komponen. Filter hanya dapat secara tidak langsung memengaruhi komponen saat:

  • Komponen disematkan dalam halaman atau tampilan.
  • Halaman atau pengontrol dan tampilan menggunakan filter.

Lihat atau unduh sampel (cara mengunduh).

Cara kerja filter

Filter berjalan dalam alur pemanggilan tindakan ASP.NET Core, kadang-kadang disebut sebagai alur filter. Alur filter berjalan setelah ASP.NET Core memilih tindakan yang akan dijalankan.

The request is processed through Other Middleware, Routing Middleware, Action Selection, and the Action Invocation Pipeline. The request processing continues back through Action Selection, Routing Middleware, and various Other Middleware before becoming a response sent to the client.

Jenis filter

Setiap jenis filter dijalankan pada tahap yang berbeda dalam alur filter:

  • Filter otorisasi berjalan terlebih dahulu dan digunakan untuk menentukan apakah pengguna berwenang untuk permintaan tersebut. Otorisasi memfilter sirkuit pendek alur jika permintaan tidak diotorisasi.

  • Filter sumber daya:

    • Jalankan setelah otorisasi.
    • OnResourceExecuting menjalankan kode sebelum alur filter lainnya. Misalnya, OnResourceExecuting menjalankan kode sebelum pengikatan model.
    • OnResourceExecuted menjalankan kode setelah alur lainnya selesai.
  • Filter tindakan:

    • Jalankan kode segera sebelum dan sesudah metode tindakan dipanggil.
    • Dapat mengubah argumen yang diteruskan ke dalam tindakan.
    • Dapat mengubah hasil yang dikembalikan dari tindakan.
    • Tidak didukung di Razor Halaman.
  • Filter pengecualian menerapkan kebijakan global ke pengecualian yang tidak tertangani yang terjadi sebelum isi respons ditulis.

  • Filter hasil menjalankan kode segera sebelum dan sesudah eksekusi hasil tindakan. Mereka hanya berjalan ketika metode tindakan berhasil dijalankan. Ini berguna untuk logika yang harus mengelilingi tampilan atau eksekusi formatter.

Diagram berikut menunjukkan bagaimana jenis filter berinteraksi dalam alur filter.

The request is processed through Authorization Filters, Resource Filters, Model Binding, Action Filters, Action Execution and Action Result Conversion, Exception Filters, Result Filters, and Result Execution. On the way out, the request is only processed by Result Filters and Resource Filters before becoming a response sent to the client.

implementasi

Filter mendukung implementasi sinkron dan asinkron melalui definisi antarmuka yang berbeda.

Filter sinkron menjalankan kode sebelum dan sesudah tahap alurnya. Misalnya, OnActionExecuting dipanggil sebelum metode tindakan dipanggil. OnActionExecuted dipanggil setelah metode tindakan kembali.

public class MySampleActionFilter : IActionFilter 
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }
}

Dalam kode sebelumnya, MyDebug adalah fungsi utilitas dalam unduhan sampel.

Filter asinkron mendefinisikan On-Stage-ExecutionAsync metode. Misalnya, OnActionExecutionAsync:

public class SampleAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context,
        ActionExecutionDelegate next)
    {
        // Do something before the action executes.

        // next() calls the action method.
        var resultContext = await next();
        // resultContext.Result is set.
        // Do something after the action executes.
    }
}

Dalam kode sebelumnya, SampleAsyncActionFilter memiliki ActionExecutionDelegate (next) yang menjalankan metode tindakan.

Beberapa tahap filter

Antarmuka untuk beberapa tahap filter dapat diimplementasikan dalam satu kelas. Misalnya, ActionFilterAttribute kelas mengimplementasikan:

Terapkan versi sinkron atau asinkron dari antarmuka filter, bukan keduanya. Runtime memeriksa terlebih dahulu untuk melihat apakah filter mengimplementasikan antarmuka asinkron, dan jika demikian, filter memanggilnya. Jika tidak, ini memanggil metode antarmuka sinkron. Jika antarmuka asinkron dan sinkron diimplementasikan dalam satu kelas, hanya metode asinkron yang dipanggil. Saat menggunakan kelas abstrak seperti ActionFilterAttribute, hanya ambil alih metode sinkron atau metode asinkron untuk setiap jenis filter.

Atribut filter bawaan

ASP.NET Core mencakup filter berbasis atribut bawaan yang dapat disubkelas dan disesuaikan. Misalnya, filter hasil berikut menambahkan header ke respons:

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 });
        base.OnResultExecuting(context);
    }
}

Atribut memungkinkan filter untuk menerima argumen, seperti yang ditunjukkan dalam contoh sebelumnya. Terapkan ke AddHeaderAttribute pengontrol atau metode tindakan dan tentukan nama dan nilai header HTTP:

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

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

Kode berikut mengimplementasikan ActionFilterAttribute bahwa:

  • Membaca judul dan nama dari sistem konfigurasi. Tidak seperti sampel sebelumnya, kode berikut tidak memerlukan parameter filter untuk ditambahkan ke kode.
  • Menambahkan judul dan nama ke header respons.
public class MyActionFilterAttribute : ActionFilterAttribute
{
    private readonly PositionOptions _settings;

    public MyActionFilterAttribute(IOptions<PositionOptions> options)
    {
        _settings = options.Value;
        Order = 1;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_settings.Title, 
                                                 new string[] { _settings.Name });
        base.OnResultExecuting(context);
    }
}

Opsi konfigurasi disediakan dari sistem konfigurasi menggunakan pola opsi. Misalnya, dari appsettings.json file:

{
  "Position": {
    "Title": "Editor",
    "Name": "Joe Smith"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

StartUp.ConfigureServicesDalam :

  • Kelas PositionOptions ditambahkan ke kontainer layanan dengan "Position" area konfigurasi.
  • MyActionFilterAttribute ditambahkan ke kontainer layanan.
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(
             Configuration.GetSection("Position"));
    services.AddScoped<MyActionFilterAttribute>();

    services.AddControllersWithViews();
}

Kode berikut menunjukkan PositionOptions kelas:

public class PositionOptions
{
    public string Title { get; set; }
    public string Name { get; set; }
}

Kode berikut menerapkan ke MyActionFilterAttributeIndex2 metode :

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ServiceFilter(typeof(MyActionFilterAttribute))]
    public IActionResult Index2()
    {
        return Content("Header values by configuration.");
    }

Di bawah Header Respons, author: Rick Anderson, dan Editor: Joe Smith ditampilkan saat Sample/Index2 titik akhir dipanggil.

Kode berikut menerapkan MyActionFilterAttribute dan ke AddHeaderAttributeRazor Halaman:

[AddHeader("Author", "Rick Anderson")]
[ServiceFilter(typeof(MyActionFilterAttribute))]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Filter tidak dapat diterapkan ke Razor metode handler Halaman. Mereka dapat diterapkan baik ke Razor model Halaman atau secara global.

Beberapa antarmuka filter memiliki atribut yang sesuai yang dapat digunakan sebagai kelas dasar untuk implementasi kustom.

Atribut filter:

Memfilter cakupan dan urutan eksekusi

Filter dapat ditambahkan ke alur di salah satu dari tiga cakupan:

  • Menggunakan atribut pada tindakan pengontrol. Atribut filter tidak dapat diterapkan ke Razor metode handler Pages.
  • Menggunakan atribut pada pengontrol atau Razor Halaman.
  • Secara global untuk semua pengontrol, tindakan, dan Razor Halaman seperti yang ditunjukkan dalam kode berikut:
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter));
    });
}

Urutan eksekusi default

Ketika ada beberapa filter untuk tahap tertentu dari alur, cakupan menentukan urutan default eksekusi filter. Filter global mengelilingi filter kelas, yang pada gilirannya mengelilingi filter metode.

Sebagai hasil dari filter bersarang, kode setelah filter berjalan dalam urutan terbalik dari kode sebelumnya . Urutan filter:

  • Kode sebelum filter global.
    • Kode sebelum pengontrol dan Razor filter Halaman.
      • Kode sebelum filter metode tindakan.
      • Kode setelah filter metode tindakan.
    • Kode setelah pengontrol dan Razor filter Halaman.
  • Kode setelah filter global.

Contoh berikut yang mengilustrasikan urutan di mana metode filter dipanggil untuk filter tindakan sinkron.

Sequence Cakupan filter Metode filter
1 Global OnActionExecuting
2 Pengontrol atau Razor Halaman OnActionExecuting
3 Metode OnActionExecuting
4 Metode OnActionExecuted
5 Pengontrol atau Razor Halaman OnActionExecuted
6 Global OnActionExecuted

Filter tingkat pengontrol

Setiap pengontrol yang mewarisi dari Controller kelas dasar mencakup Controller.OnActionExecutingmetode , Controller.OnActionExecutionAsync, dan Controller.OnActionExecutedOnActionExecuted . Metode ini:

  • Bungkus filter yang berjalan untuk tindakan tertentu.
  • OnActionExecuting dipanggil sebelum salah satu filter tindakan.
  • OnActionExecuted dipanggil setelah semua filter tindakan.
  • OnActionExecutionAsync dipanggil sebelum salah satu filter tindakan. Kode dalam filter setelah next berjalan setelah metode tindakan.

Misalnya, dalam sampel unduhan, MySampleActionFilter diterapkan secara global dalam startup.

TestController:

  • SampleActionFilterAttribute Menerapkan ([SampleActionFilter]) ke FilterTest2 tindakan.
  • Mengambil alih OnActionExecuting dan OnActionExecuted.
public class TestController : Controller
{
    [SampleActionFilter(Order = int.MinValue)]
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

MyDisplayRouteInfo disediakan oleh paket Rick.Docs.Samples.RouteInfo NuGet dan menampilkan informasi rute.

Menavigasi untuk https://localhost:5001/Test/FilterTest2 menjalankan kode berikut:

  • TestController.OnActionExecuting
    • MySampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • TestController.FilterTest2
      • SampleActionFilterAttribute.OnActionExecuted
    • MySampleActionFilter.OnActionExecuted
  • TestController.OnActionExecuted

Filter tingkat pengontrol mengatur properti Pesanan ke int.MinValue. Filter tingkat pengontrol tidak dapat diatur untuk dijalankan setelah filter diterapkan ke metode. Pesanan dijelaskan di bagian berikutnya.

Untuk Razor Halaman, lihat Menerapkan Razor filter Halaman dengan menimpa metode filter.

Mengesampingkan urutan default

Urutan default eksekusi dapat diganti dengan menerapkan IOrderedFilter. IOrderedFilterOrder mengekspos properti yang lebih diutamakan daripada cakupan untuk menentukan urutan eksekusi. Filter dengan nilai yang lebih rendah Order :

  • Menjalankan kode sebelum filter dengan nilai yang lebih tinggi dari Order.
  • Menjalankan kode setelah itu dari filter dengan nilai yang lebih tinggiOrder.

Properti Order diatur dengan parameter konstruktor:

[SampleActionFilter(Order = int.MinValue)]

Pertimbangkan dua filter tindakan di pengontrol berikut:

[MyAction2Filter]
public class Test2Controller : Controller
{
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

Filter global ditambahkan di StartUp.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter));
    });
}

3 filter berjalan dalam urutan berikut:

  • Test2Controller.OnActionExecuting
    • MySampleActionFilter.OnActionExecuting
      • MyAction2FilterAttribute.OnActionExecuting
        • Test2Controller.FilterTest2
      • MyAction2FilterAttribute.OnResultExecuting
    • MySampleActionFilter.OnActionExecuted
  • Test2Controller.OnActionExecuted

Properti Order mengambil alih cakupan saat menentukan urutan filter berjalan. Filter diurutkan terlebih dahulu menurut urutan, lalu cakupan digunakan untuk memutuskan ikatan. Semua filter bawaan mengimplementasikan IOrderedFilter dan mengatur nilai default Order ke 0. Seperti disebutkan sebelumnya, filter tingkat pengontrol mengatur properti Pesanan ke int.MinValue Untuk filter bawaan, cakupan menentukan urutan kecuali Order diatur ke nilai bukan nol.

Dalam kode sebelumnya, MySampleActionFilter memiliki cakupan global sehingga berjalan sebelum MyAction2FilterAttribute, yang memiliki cakupan pengontrol. Untuk menjalankan MyAction2FilterAttribute terlebih dahulu, atur pesanan ke int.MinValue:

[MyAction2Filter(int.MinValue)]
public class Test2Controller : Controller
{
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

Untuk membuat filter MySampleActionFilter global berjalan terlebih dahulu, atur Order ke int.MinValue:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter),
                            int.MinValue);
    });
}

Pembatalan dan sirkuit pendek

Alur filter dapat disusun pendek dengan mengatur Result properti pada parameter yang ResourceExecutingContext disediakan untuk metode filter. Misalnya, filter Sumber Daya berikut mencegah sisa alur dieksekusi:

public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        context.Result = new ContentResult()
        {
            Content = "Resource unavailable - header not set."
        };
    }

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
    }
}

Dalam kode berikut, dan ShortCircuitingResourceFilterAddHeader filter menargetkan SomeResource metode tindakan. ShortCircuitingResourceFilter:

  • Berjalan terlebih dahulu, karena ini adalah Filter Sumber Daya dan AddHeader merupakan Filter Tindakan.
  • Sirkuit pendek sisa alur.

AddHeader Oleh karena itu filter tidak pernah berjalan untuk tindakan.SomeResource Perilaku ini akan sama jika kedua filter diterapkan pada tingkat metode tindakan, asalkan ShortCircuitingResourceFilter dijalankan terlebih dahulu. ShortCircuitingResourceFilter Eksekusi pertama karena jenis filternya, atau dengan penggunaan Order properti secara eksplisit.

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ServiceFilter(typeof(MyActionFilterAttribute))]
    public IActionResult Index2()
    {
        return Content("Header values by configuration.");
    }

    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header is set.");
    }

    [AddHeaderWithFactory]
    public IActionResult HeaderWithFactory()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }
}

Injeksi dependensi

Filter dapat ditambahkan berdasarkan jenis atau berdasarkan instans. Jika instans ditambahkan, instans tersebut digunakan untuk setiap permintaan. Jika jenis ditambahkan, jenis diaktifkan. Filter yang diaktifkan jenis berarti:

  • Instans dibuat untuk setiap permintaan.
  • Setiap dependensi konstruktor diisi oleh injeksi dependensi (DI).

Filter yang diimplementasikan sebagai atribut dan ditambahkan langsung ke kelas pengontrol atau metode tindakan tidak dapat memiliki dependensi konstruktor yang disediakan oleh injeksi dependensi (DI). Dependensi konstruktor tidak dapat disediakan oleh DI karena:

  • Atribut harus memiliki parameter konstruktor yang disediakan tempat parameter tersebut diterapkan.
  • Ini adalah batasan cara kerja atribut.

Filter berikut mendukung dependensi konstruktor yang disediakan dari DI:

Filter sebelumnya dapat diterapkan ke pengontrol atau metode tindakan:

Pencatat tersedia dari DI. Namun, hindari membuat dan menggunakan filter murni untuk tujuan pengelogan. Pengelogan kerangka kerja bawaan biasanya menyediakan apa yang diperlukan untuk pengelogan. Pengelogan ditambahkan ke filter:

  • Harus fokus pada masalah atau perilaku domain bisnis khusus untuk filter.
  • Tidak boleh mencatat tindakan atau peristiwa kerangka kerja lainnya. Filter bawaan memfilter tindakan log dan peristiwa kerangka kerja.

ServiceFilterAttribute

Jenis implementasi filter layanan terdaftar di ConfigureServices. Mengambil ServiceFilterAttribute instans filter dari DI.

Kode berikut menunjukkan AddHeaderResultServiceFilter:

public class AddHeaderResultServiceFilter : IResultFilter
{
    private ILogger _logger;
    public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var headerName = "OnResultExecuting";
        context.HttpContext.Response.Headers.Add(
            headerName, new string[] { "ResultExecutingSuccessfully" });
        _logger.LogInformation("Header added: {HeaderName}", headerName);
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
        _logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
    }
}

Dalam kode berikut, AddHeaderResultServiceFilter ditambahkan ke kontainer DI:

public void ConfigureServices(IServiceCollection services)
{
    // Add service filters.
    services.AddScoped<AddHeaderResultServiceFilter>();
    services.AddScoped<SampleActionFilterAttribute>();

    services.AddControllersWithViews(options =>
   {
       options.Filters.Add(new AddHeaderAttribute("GlobalAddHeader",
           "Result filter added to MvcOptions.Filters"));         // An instance
        options.Filters.Add(typeof(MySampleActionFilter));         // By type
        options.Filters.Add(new SampleGlobalActionFilter());       // An instance
    });
}

Dalam kode berikut, ServiceFilter atribut mengambil instans AddHeaderResultServiceFilter filter dari DI:

[ServiceFilter(typeof(AddHeaderResultServiceFilter))]
public IActionResult Index()
{
    return View();
}

Saat menggunakan ServiceFilterAttribute, pengaturan ServiceFilterAttribute.IsReusable:

  • Memberikan petunjuk bahwa instans filter dapat digunakan kembali di luar cakupan permintaan tempat instans dibuat. Runtime ASP.NET Core tidak menjamin:

    • Bahwa satu instans filter akan dibuat.
    • Filter tidak akan diminta kembali dari kontainer DI di beberapa titik kemudian.
  • Tidak boleh digunakan dengan filter yang bergantung pada layanan dengan masa pakai selain singleton.

ServiceFilterAttribute penerapan IFilterFactory. IFilterFactorymengekspos metode untuk membuat IFilterMetadata instansCreateInstance. CreateInstance memuat jenis yang ditentukan dari DI.

TypeFilterAttribute

TypeFilterAttribute mirip ServiceFilterAttributedengan , tetapi jenisnya tidak diselesaikan langsung dari kontainer DI. Ini membuat instans jenis dengan menggunakan Microsoft.Extensions.DependencyInjection.ObjectFactory.

Karena TypeFilterAttribute jenis tidak diselesaikan langsung dari kontainer DI:

  • Jenis yang direferensikan menggunakan TypeFilterAttribute tidak perlu didaftarkan dengan kontainer DI. Mereka memang memiliki dependensi yang dipenuhi oleh kontainer DI.
  • TypeFilterAttribute dapat secara opsional menerima argumen konstruktor untuk jenis tersebut.

Saat menggunakan TypeFilterAttribute, pengaturan TypeFilterAttribute.IsReusable:

  • Memberikan petunjuk bahwa instans filter dapat digunakan kembali di luar cakupan permintaan tempat instans dibuat. Runtime ASP.NET Core tidak memberikan jaminan bahwa satu instans filter akan dibuat.

  • Tidak boleh digunakan dengan filter yang bergantung pada layanan dengan masa pakai selain singleton.

Contoh berikut menunjukkan cara meneruskan argumen ke jenis menggunakan TypeFilterAttribute:

[TypeFilter(typeof(LogConstantFilter),
    Arguments = new object[] { "Method 'Hi' called" })]
public IActionResult Hi(string name)
{
    return Content($"Hi {name}");
}

Filter otorisasi

Filter otorisasi:

  • Apakah filter pertama dijalankan di alur filter.
  • Mengontrol akses ke metode tindakan.
  • Memiliki metode sebelumnya, tetapi tidak setelah metode.

Filter otorisasi kustom memerlukan kerangka kerja otorisasi kustom. Lebih suka mengonfigurasi kebijakan otorisasi atau menulis kebijakan otorisasi kustom daripada menulis filter kustom. Filter otorisasi bawaan:

  • Memanggil sistem otorisasi.
  • Tidak mengotorisasi permintaan.

Jangan melemparkan pengecualian dalam filter otorisasi:

  • Pengecualian tidak akan ditangani.
  • Filter pengecualian tidak akan menangani pengecualian.

Pertimbangkan untuk mengeluarkan tantangan saat pengecualian terjadi di filter otorisasi.

Pelajari selengkapnya tentang Otorisasi.

Filter sumber daya

Filter sumber daya:

Filter sumber daya berguna untuk sirkuit pendek sebagian besar alur. Misalnya, filter penembolokan dapat menghindari sisa alur pada hit cache.

Contoh filter sumber daya:

  • Filter sumber daya sirkuit pendek yang ditunjukkan sebelumnya.

  • DisableFormValueModelBindingAttribute:

    • Mencegah pengikatan model mengakses data formulir.
    • Digunakan untuk unggahan file besar untuk mencegah data formulir dibaca ke dalam memori.

Filter tindakan

Filter tindakan tidak berlaku untuk Razor Halaman. Razor Pages mendukung IPageFilter dan IAsyncPageFilter. Untuk informasi selengkapnya, lihat Metode filter untuk Razor Halaman.

Filter tindakan:

Kode berikut menunjukkan filter tindakan sampel:

public class MySampleActionFilter : IActionFilter 
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }
}

menyediakan ActionExecutingContext properti berikut:

  • ActionArguments - memungkinkan membaca input ke metode tindakan.
  • Controller - memungkinkan memanipulasi instans pengontrol.
  • Result - mengatur Result eksekusi sirkuit pendek dari metode tindakan dan filter tindakan berikutnya.

Melemparkan pengecualian dalam metode tindakan:

  • Mencegah berjalannya filter berikutnya.
  • Tidak seperti pengaturan Result, diperlakukan sebagai kegagalan alih-alih hasil yang berhasil.

menyediakan ActionExecutedContextController dan Result ditambah properti berikut:

  • Canceled - Benar jika eksekusi tindakan disusun pendek oleh filter lain.

  • Exception - Non-null jika tindakan atau filter tindakan yang dijalankan sebelumnya melemparkan pengecualian. Mengatur properti ini ke null:

    • Menangani pengecualian secara efektif.
    • Result dijalankan seolah-olah dikembalikan dari metode tindakan.

IAsyncActionFilterUntuk , panggilan ke ActionExecutionDelegate:

  • Menjalankan filter tindakan berikutnya dan metode tindakan.
  • Menampilkan ActionExecutedContext.

Untuk sirkuit pendek, tetapkan Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result ke instans hasil dan jangan panggil next ().ActionExecutionDelegate

Kerangka kerja menyediakan abstrak ActionFilterAttribute yang dapat disubkelas.

OnActionExecuting Filter tindakan dapat digunakan untuk:

  • Memvalidasi status model.
  • Mengembalikan kesalahan jika status tidak valid.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext 
                                           context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(
                                                context.ModelState);
        }
    }

Catatan

Pengontrol yang dianotasi dengan [ApiController] atribut secara otomatis memvalidasi status model dan mengembalikan respons 400. Untuk informasi selengkapnya, lihat Respons HTTP 400 otomatis. Metode OnActionExecuted berjalan setelah metode tindakan:

  • Dan dapat melihat dan memanipulasi hasil tindakan melalui Result properti .

  • Canceled diatur ke true jika eksekusi tindakan disusun pendek oleh filter lain.

  • Exception diatur ke nilai non-null jika tindakan atau filter tindakan berikutnya melemparkan pengecualian. Pengaturan Exception ke null:

    • Menangani pengecualian secara efektif.
    • ActionExecutedContext.Result dijalankan seolah-olah dikembalikan secara normal dari metode tindakan.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext 
                                           context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(
                                                context.ModelState);
        }
    }


    public override void OnActionExecuted(ActionExecutedContext 
                                          context)
    {
        var result = context.Result;
        // Do something with Result.
        if (context.Canceled == true)
        {
            // Action execution was short-circuited by another filter.
        }

        if(context.Exception != null)
        {
            // Exception thrown by action or action filter.
            // Set to null to handle the exception.
            context.Exception = null;
        }
        base.OnActionExecuted(context);
    }
}

Filter pengecualian

Filter pengecualian:

Filter pengecualian sampel berikut menggunakan tampilan kesalahan kustom untuk menampilkan detail tentang pengecualian yang terjadi saat aplikasi sedang dalam pengembangan:

public class CustomExceptionFilter : IExceptionFilter
{
    private readonly IWebHostEnvironment _hostingEnvironment;
    private readonly IModelMetadataProvider _modelMetadataProvider;

    public CustomExceptionFilter(
        IWebHostEnvironment hostingEnvironment,
        IModelMetadataProvider modelMetadataProvider)
    {
        _hostingEnvironment = hostingEnvironment;
        _modelMetadataProvider = modelMetadataProvider;
    }

    public void OnException(ExceptionContext context)
    {
        if (!_hostingEnvironment.IsDevelopment())
        {
            return;
        }
        var result = new ViewResult {ViewName = "CustomError"};
        result.ViewData = new ViewDataDictionary(_modelMetadataProvider,
                                                    context.ModelState);
        result.ViewData.Add("Exception", context.Exception);
        // TODO: Pass additional detailed data via ViewData
        context.Result = result;
    }
}

Kode berikut menguji filter pengecualian:

[TypeFilter(typeof(CustomExceptionFilter))]
public class FailingController : Controller
{
    [AddHeader("Failing Controller", 
               "Won't appear when exception is handled")]
    public IActionResult Index()
    {
        throw new Exception("Testing custom exception filter.");
    }
}

Filter pengecualian:

  • Tidak memiliki peristiwa sebelum dan sesudah.
  • Terapkan OnException atau OnExceptionAsync.
  • Tangani pengecualian yang tidak tertangani yang terjadi di Razor Pembuatan halaman atau pengontrol, pengikatan model, filter tindakan, atau metode tindakan.
  • Jangan menangkap pengecualian yang terjadi dalam filter sumber daya, filter hasil, atau eksekusi hasil MVC.

Untuk menangani pengecualian, atur properti ke ExceptionHandledtrue atau tetapkan Result properti . Ini menghentikan penyebaran pengecualian. Filter pengecualian tidak dapat mengubah pengecualian menjadi "sukses". Hanya filter tindakan yang dapat melakukannya.

Filter pengecualian:

  • Baik untuk menjebak pengecualian yang terjadi dalam tindakan.
  • Tidak fleksibel seperti middleware penanganan kesalahan.

Lebih suka middleware untuk penanganan pengecualian. Gunakan filter pengecualian hanya di mana penanganan kesalahan berbeda berdasarkan metode tindakan mana yang dipanggil. Misalnya, aplikasi mungkin memiliki metode tindakan untuk titik akhir API dan untuk tampilan/HTML. Titik akhir API dapat mengembalikan informasi kesalahan sebagai JSAKTIF, sementara tindakan berbasis tampilan dapat mengembalikan halaman kesalahan sebagai HTML.

Filter hasil

Filter hasil:

IResultFilter dan IAsyncResultFilter

Kode berikut menunjukkan filter hasil yang menambahkan header HTTP:

public class AddHeaderResultServiceFilter : IResultFilter
{
    private ILogger _logger;
    public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var headerName = "OnResultExecuting";
        context.HttpContext.Response.Headers.Add(
            headerName, new string[] { "ResultExecutingSuccessfully" });
        _logger.LogInformation("Header added: {HeaderName}", headerName);
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
        _logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
    }
}

Jenis hasil yang dijalankan tergantung pada tindakan. Tindakan yang mengembalikan tampilan mencakup semua pemrosesan pisau cukur sebagai bagian dari yang ViewResult dijalankan. Metode API mungkin melakukan beberapa serialisasi sebagai bagian dari eksekusi hasilnya. Pelajari selengkapnya tentang hasil tindakan.

Filter hasil hanya dijalankan saat filter tindakan atau tindakan menghasilkan hasil tindakan. Filter hasil tidak dijalankan ketika:

  • Filter otorisasi atau filter sumber daya sirkuit pendek alur.
  • Filter pengecualian menangani pengecualian dengan menghasilkan hasil tindakan.

Metode ini Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting dapat melakukan eksekusi sirkuit pendek dari hasil tindakan dan filter hasil berikutnya dengan mengatur Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel ke true. Tulis ke objek respons saat sirkuit pendek untuk menghindari menghasilkan respons kosong. Melemparkan pengecualian di IResultFilter.OnResultExecuting:

  • Mencegah eksekusi hasil tindakan dan filter berikutnya.
  • Diperlakukan sebagai kegagalan alih-alih hasil yang berhasil.

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted Ketika metode berjalan, respons mungkin sudah dikirim ke klien. Jika respons telah dikirim ke klien, respons tidak dapat diubah.

ResultExecutedContext.Canceled diatur ke true jika eksekusi hasil tindakan disusun pendek oleh filter lain.

ResultExecutedContext.Exception diatur ke nilai non-null jika hasil tindakan atau filter hasil berikutnya melemparkan pengecualian. Pengaturan Exception ke null secara efektif menangani pengecualian dan mencegah pengecualian dilemparkan lagi nanti di alur. Tidak ada cara yang dapat diandalkan untuk menulis data ke respons saat menangani pengecualian dalam filter hasil. Jika header telah dibersihkan ke klien ketika hasil tindakan melemparkan pengecualian, tidak ada mekanisme yang dapat diandalkan untuk mengirim kode kegagalan.

IAsyncResultFilterUntuk , panggilan ke await nextResultExecutionDelegate pada menjalankan filter hasil berikutnya dan hasil tindakan. Untuk sirkuit pendek, atur ResultExecutingContext.Cancel ke true dan jangan panggil ResultExecutionDelegate:

public class MyAsyncResponseFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(ResultExecutingContext context,
                                             ResultExecutionDelegate next)
    {
        if (!(context.Result is EmptyResult))
        {
            await next();
        }
        else
        {
            context.Cancel = true;
        }

    }
}

Kerangka kerja menyediakan abstrak ResultFilterAttribute yang dapat disubkelas. Kelas AddHeaderAttribute yang ditampilkan sebelumnya adalah contoh atribut filter hasil.

IAlwaysRunResultFilter dan IAsyncAlwaysRunResultFilter

Antarmuka IAlwaysRunResultFilter dan IAsyncAlwaysRunResultFilter mendeklarasikan IResultFilter implementasi yang berjalan untuk semua hasil tindakan. Ini termasuk hasil tindakan yang dihasilkan oleh:

  • Filter otorisasi dan filter sumber daya yang memiliki sirkuit pendek.
  • Filter pengecualian.

Misalnya, filter berikut selalu menjalankan dan menetapkan hasil tindakan (ObjectResult) dengan kode status Entitas Tidak Dapat Diproses 422 saat negosiasi konten gagal:

public class UnprocessableResultFilter : Attribute, IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is StatusCodeResult statusCodeResult &&
            statusCodeResult.StatusCode == (int) HttpStatusCode.UnsupportedMediaType)
        {
            context.Result = new ObjectResult("Can't process this!")
            {
                StatusCode = (int) HttpStatusCode.UnsupportedMediaType,
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
    }
}

IFilterFactory

IFilterFactory penerapan IFilterMetadata. Oleh karena itu, IFilterFactory instans dapat digunakan sebagai IFilterMetadata instans di mana saja dalam alur filter. Ketika runtime bersiap untuk memanggil filter, runtime mencoba untuk melemparkannya ke IFilterFactory. Jika transmisi tersebut berhasil, CreateInstance metode dipanggil untuk membuat IFilterMetadata instans yang dipanggil. Ini menyediakan desain yang fleksibel, karena alur filter yang tepat tidak perlu diatur secara eksplisit saat aplikasi dimulai.

IFilterFactory.IsReusable:

  • Adalah petunjuk dari pabrik bahwa instans filter yang dibuat oleh pabrik dapat digunakan kembali di luar cakupan permintaan tempat instans dibuat.
  • Tidak boleh digunakan dengan filter yang bergantung pada layanan dengan masa pakai selain singleton.

Runtime ASP.NET Core tidak menjamin:

  • Bahwa satu instans filter akan dibuat.
  • Filter tidak akan diminta kembali dari kontainer DI di beberapa titik kemudian.

Peringatan

Hanya konfigurasikan IFilterFactory.IsReusable untuk mengembalikan true jika sumber filter tidak ambigu, filter tidak bersifat stateless, dan filter aman digunakan di beberapa permintaan HTTP. Misalnya, jangan mengembalikan filter dari DI yang terdaftar sebagai tercakup atau sementara jika IFilterFactory.IsReusable mengembalikan true. IFilterFactory dapat diimplementasikan menggunakan implementasi atribut kustom sebagai pendekatan lain untuk membuat filter:

public class AddHeaderWithFactoryAttribute : Attribute, IFilterFactory
{
    // Implement IFilterFactory
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return new InternalAddHeaderFilter();
    }

    private class InternalAddHeaderFilter : IResultFilter
    {
        public void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                "Internal", new string[] { "My header" });
        }

        public void OnResultExecuted(ResultExecutedContext context)
        {
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

Filter diterapkan dalam kode berikut:

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ServiceFilter(typeof(MyActionFilterAttribute))]
    public IActionResult Index2()
    {
        return Content("Header values by configuration.");
    }

    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header is set.");
    }

    [AddHeaderWithFactory]
    public IActionResult HeaderWithFactory()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }
}

Uji kode sebelumnya dengan menjalankan sampel unduhan:

  • Panggil alat pengembang F12.
  • Buka https://localhost:5001/Sample/HeaderWithFactory.

Alat pengembang F12 menampilkan header respons berikut yang ditambahkan oleh kode sampel:

  • Penulis:Rick Anderson
  • globaladdheader:Result filter added to MvcOptions.Filters
  • Internal:My header

Kode sebelumnya membuat header internal:My header respons.

IFilterFactory diimplementasikan pada atribut

Filter yang diterapkan IFilterFactory berguna untuk filter yang:

  • Tidak memerlukan parameter passing.
  • Memiliki dependensi konstruktor yang perlu diisi oleh DI.

TypeFilterAttribute penerapan IFilterFactory. IFilterFactorymengekspos metode untuk membuat IFilterMetadata instansCreateInstance. CreateInstance memuat jenis yang ditentukan dari kontainer layanan (DI).

public class SampleActionFilterAttribute : TypeFilterAttribute
{
    public SampleActionFilterAttribute()
                         :base(typeof(SampleActionFilterImpl))
    { 
    }

    private class SampleActionFilterImpl : IActionFilter
    {
        private readonly ILogger _logger;
        public SampleActionFilterImpl(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<SampleActionFilterAttribute>();
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
           _logger.LogInformation("SampleActionFilterAttribute.OnActionExecuting");
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _logger.LogInformation("SampleActionFilterAttribute.OnActionExecuted");
        }
    }
}

Kode berikut menunjukkan tiga pendekatan untuk menerapkan [SampleActionFilter]:

[SampleActionFilter]
public IActionResult FilterTest()
{
    return Content("From FilterTest");
}

[TypeFilter(typeof(SampleActionFilterAttribute))]
public IActionResult TypeFilterTest()
{
    return Content("From TypeFilterTest");
}

// ServiceFilter must be registered in ConfigureServices or
// System.InvalidOperationException: No service for type '<filter>'
// has been registered. Is thrown.
[ServiceFilter(typeof(SampleActionFilterAttribute))]
public IActionResult ServiceFilterTest()
{
    return Content("From ServiceFilterTest");
}

Dalam kode sebelumnya, mendekorasi metode dengan [SampleActionFilter] adalah pendekatan yang disukai untuk menerapkan SampleActionFilter.

Menggunakan middleware dalam alur filter

Filter sumber daya berfungsi seperti middleware karena mereka mengelilingi eksekusi semua yang muncul nanti dalam alur. Tetapi filter berbeda dari middleware karena mereka adalah bagian dari runtime, yang berarti bahwa mereka memiliki akses ke konteks dan konstruksi.

Untuk menggunakan middleware sebagai filter, buat jenis dengan Configure metode yang menentukan middleware untuk dimasukkan ke dalam alur filter. Contoh berikut menggunakan middleware pelokalan untuk membangun budaya saat ini untuk permintaan:

public class LocalizationPipeline
{
    public void Configure(IApplicationBuilder applicationBuilder)
    {
        var supportedCultures = new[]
        {
            new CultureInfo("en-US"),
            new CultureInfo("fr")
        };

        var options = new RequestLocalizationOptions
        {
            DefaultRequestCulture = new RequestCulture(
                                       culture: "en-US", 
                                       uiCulture: "en-US"),
            SupportedCultures = supportedCultures,
            SupportedUICultures = supportedCultures
        };
        options.RequestCultureProviders = new[] 
            { new RouteDataRequestCultureProvider() {
                Options = options } };

        applicationBuilder.UseRequestLocalization(options);
    }
}

MiddlewareFilterAttribute Gunakan untuk menjalankan middleware:

[Route("{culture}/[controller]/[action]")]
[MiddlewareFilter(typeof(LocalizationPipeline))]
public IActionResult CultureFromRouteData()
{
    return Content(
          $"CurrentCulture:{CultureInfo.CurrentCulture.Name},"
        + $"CurrentUICulture:{CultureInfo.CurrentUICulture.Name}");
}

Filter middleware berjalan pada tahap alur filter yang sama dengan filter Sumber Daya, sebelum pengikatan model dan setelah alur lainnya.

Keamanan utas

Saat meneruskan instans filter ke , Addalih-alih Type, filternya adalah singleton dan tidak aman untuk utas.

Tindakan berikutnya