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, memotong jalur permintaan untuk mengembalikan respons yang telah di-cache.

Filter kustom dapat dibuat untuk menangani kekhawatiran lintas fungsi. Contoh concern lintas proses termasuk penanganan kesalahan, penyimpanan sementara, konfigurasi, otorisasi, dan pencatatan. 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, pengontrol, dan tampilan menggunakan filter.

Cara kerja filter

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

Permintaan diproses melalui Middleware Lainnya, Middleware Perutean, Pemilihan Tindakan, dan Alur Pemanggilan Tindakan. Pemrosesan permintaan berlanjut kembali melalui Pemilihan Tindakan, Middleware Perutean, dan berbagai Middleware Lainnya sebelum menjadi respons yang dikirim ke klien.

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.
    • Hentikan proses jika permintaan tidak terotorisasi.
  • Filter sumber daya:

    • Jalankan setelah otorisasi.
    • OnResourceExecuting menjalankan kode sebelum alur pemrosesan filter lainnya mulai. Misalnya, OnResourceExecuting menjalankan kode sebelum pengikatan model.
    • OnResourceExecuted menjalankan program setelah tahap lain dari alur selesai.
  • Filter aksi:

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

    • Jalankan segera sebelum dan sesudah metode tindakan dipanggil.
    • Dapat mengubah argumen yang diteruskan ke tindakan.
    • Dapat mengubah hasil yang dihasilkan oleh tindakan.
    • Tidak didukung di Razor Halaman.
    • Dapat dipanggil pada tindakan dan titik akhir berbasis handler rute.
  • Filter pengecualian:

    • Terapkan kebijakan global ke pengecualian yang tidak tertangani yang terjadi sebelum isi respons ditulis.
    • Jalankan setelah pengikatan model dan filter tindakan, tetapi sebelum hasil tindakan dijalankan.
    • Jalankan hanya jika pengecualian yang tidak tertangani terjadi selama pelaksanaan tindakan atau hasil tindakan.
    • Jangan tangani pengecualian yang dilemparkan selama eksekusi middleware, perutean, atau pengikatan model.
  • Filter hasil:

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

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 aksi mengembalikan hasil:

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 metode On-Stage-ExecutionAsync. 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 ResponseHeaderAttribute ke pengontrol atau metode tindakan dan tentukan nama serta 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 Response Headers, 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.");
}

Respon dari tindakan Multiple mencakup tajuk 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.

Filter Atribut:

Filter tidak dapat diterapkan ke metode handler halaman Razor. Mereka dapat diterapkan baik secara global maupun ke Model Halaman Razor.

Cakupan penyaringan 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 kerja, cakupan menentukan urutan default dari eksekusi filter. Filter global mengelilingi filter kelas, yang pada gilirannya mengelilingi filter metode.

Akibat dari penyaringan bersarang, kode setelah filter berjalan dalam urutan terbalik dari kode sebelum. Urutan filter:

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

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

Urutan Cakupan filter Metode filter
1 Mendunia OnActionExecuting
2 Pengontrol OnActionExecuting
3 Perbuatan OnActionExecuting
4 Perbuatan OnActionExecuted
5 Pengontrol OnActionExecuted
6 Mendunia OnActionExecuted

Filter pada tingkat pengendali

Setiap pengontrol yang mewarisi dari Controller mencakup metode OnActionExecuting, OnActionExecutionAsync, dan OnActionExecuted. Metode ini membungkus filter yang dijalankan untuk tindakan tertentu.

  • OnActionExecuting berjalan sebelum filter-filter tindakan apa pun.
  • OnActionExecuted berjalan setelah semua filter tindakan.
  • OnActionExecutionAsync berjalan sebelum filter-filter tindakan apa pun. Kode setelah pemanggilan ke next berjalan setelah filter tindakan.

Kelas berikut ControllerFiltersController :

  • Menerapkan SampleActionFilterAttribute ([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 ke 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 yang diterapkan pada metode. Pesanan dijelaskan di bagian berikutnya.

Untuk Razor Halaman, lihat Razor.

Mengatasi urutan default

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

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

Dalam contoh filter tingkat pengontrol, memiliki cakupan global sehingga berjalan sebelum GlobalSampleActionFilter, 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 Pemutusan Arus Pendek

Alur filter dapat diputus dengan mengatur properti Result pada parameter yang disediakan oleh ResourceExecutingContext 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, baik filter [ShortCircuitingResourceFilter] maupun [ResponseHeader] menargetkan metode tindakan Index. Filter ShortCircuitingResourceFilterAttribute :

  • Berjalan terlebih dahulu, karena ini adalah Filter Sumber Daya dan ResponseHeaderAttribute merupakan Filter Tindakan.
  • Menghentikan sisa proses.

Oleh karena itu, filter ResponseHeaderAttribute tidak pernah berjalan untuk tindakan Index. Perilaku ini akan sama jika kedua filter diterapkan pada tingkat metode tindakan, asalkan ShortCircuitingResourceFilterAttribute dijalankan terlebih dahulu. ShortCircuitingResourceFilterAttribute berjalan lebih dahulu 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 sebuah tipe ditambahkan, maka tipe tersebut akan diaktifkan. Filter yang diaktifkan berdasarkan 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.

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

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

Atribut Penyaring Layanan

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, atribut ServiceFilter mengambil contoh filter LoggingResponseHeaderFilterService dari DI.

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

Saat menggunakan ServiceFilterAttribute, menyetel ServiceFilterAttribute.IsReusable:

  • Memberikan indikasi bahwa instans filter dapat digunakan kembali di luar cakupan permintaan tempat pembuatannya. 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 yang berbeda dari singleton.

ServiceFilterAttribute menerapkan IFilterFactory. IFilterFactory mengekspos metode CreateInstance untuk membuat instans IFilterMetadata. CreateInstance memuat tipe yang ditentukan dari DI.

TypeFilterAttribute

TypeFilterAttribute mirip dengan ServiceFilterAttribute, tetapi tipe TypeFilterAttribute tidak diambil langsung dari kontainer DI. Ini menginstansiasi 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, menyetel TypeFilterAttribute.IsReusable:

  • Memberikan indikasi bahwa instans filter dapat digunakan kembali di luar cakupan permintaan asal pembuatannya. 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 'before', tetapi tidak memiliki metode 'after'.

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 melempar 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:

Pemfilteran sumber daya berguna untuk memotong sebagian besar alur. Misalnya, filter penyimpanan sementara dapat menghindari sisa proses saat terjadi hit cache.

Contoh filter sumber daya:

Penyaring 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.
    }
}

ActionExecutingContext menyediakan properti berikut:

  • ActionArguments - memungkinkan untuk membaca masukan ke dalam 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 penerapan filter berikutnya.
  • Tidak seperti pengaturan Result, ini diperlakukan sebagai kegagalan, bukan hasil sukses.

menyediakan ActionExecutedContextController dan Result ditambah properti berikut:

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

Untuk IAsyncActionFilter, panggilan ke ActionExecutionDelegate:

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

Untuk memutus rangkaian, tetapkan Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result ke instance hasil dan jangan panggil next (ActionExecutionDelegate).

Kerangka kerja menyediakan abstrak ActionFilterAttribute yang dapat disubkelas.

OnActionExecuting Filter tindakan dapat digunakan untuk:

  • Validasi 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 dijalankan setelah metode aksi:

  • Dan dapat melihat dan memanipulasi hasil tindakan melalui Result properti .
  • Canceled diatur ke true jika eksekusi tindakan dibatalkan 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 hasilnya dikembalikan secara normal dari metode tersebut.

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 tidak tertangani yang terjadi saat pembuatan Razor halaman atau pengontrol, pengikatan model, filter tindakan, atau metode tindakan.
  • Jangan menangkap pengecualian yang terjadi pada filter sumber daya, filter hasil, atau eksekusi hasil MVC.

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

Filter pengecualian:

  • Baik untuk menangkap pengecualian yang terjadi dalam aksi.
  • 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 JSON, 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.
    }
}

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

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

  • Filter otorisasi atau filter sumber daya memotong alur proses.
  • Filter pengecualian menangani pengecualian dengan menghasilkan hasil tindakan.

Metode Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting dapat menghentikan eksekusi lebih awal 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 menjadi true jika eksekusi hasil aksi dihentikan oleh filter lain.

ResultExecutedContext.Exception disetel ke dalam nilai non-null jika hasil tindakan atau filter hasil berikutnya memunculkan 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 sudah dikirim ke klien ketika hasil tindakan menghasilkan pengecualian, tidak ada mekanisme yang dapat diandalkan untuk mengirimkan kode kegagalan.

Untuk IAsyncResultFilter, pemanggilan await next pada ResultExecutionDelegate menjalankan penyaring 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 menghentikan proses di tengah jalan.
  • 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 menerapkan IFilterMetadata. Oleh karena itu, IFilterFactory instans dapat digunakan sebagai IFilterMetadata instans di mana saja dalam alur filter. Ketika runtime bersiap untuk memanggil filter, runtime berusaha mengonversinya 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 fabrik bahwa instance filter yang dibuat oleh fabrik dapat digunakan kembali di luar lingkup permintaan tempat instance tersebut dibuat.
  • Jangan digunakan dengan filter yang bergantung pada layanan dengan masa pakai yang berbeda dari singleton.

ASP.NET Core runtime 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 bersifat stateless, dan filter aman digunakan di berbagai 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 mengimplementasikan IFilterFactory berguna untuk filter yang:

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

TypeFilterAttribute menerapkan IFilterFactory. IFilterFactory mengekspos metode CreateInstance untuk membuat instans IFilterMetadata. CreateInstance memuat tipe yang ditentukan dari wadah 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.

Gunakan 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 tipe dengan metode Configure yang menentukan middleware untuk dimasukkan ke dalam rangkaian 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 Thread

Saat meneruskan instans dari filter ke Add, bukan ke Type, filter tersebut menjadi singleton dan tidak aman dalam konteks multi-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, memotong jalur permintaan untuk mengembalikan respons yang telah di-cache.

Filter kustom dapat dibuat untuk menangani kekhawatiran lintas fungsi. Contoh concern lintas proses termasuk penanganan kesalahan, penyimpanan sementara, konfigurasi, otorisasi, dan pencatatan. 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, pengontrol, dan tampilan menggunakan filter.

Cara kerja filter

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

Permintaan diproses melalui Middleware Lainnya, Middleware Perutean, Pemilihan Tindakan, dan Alur Pemanggilan Tindakan. Pemrosesan permintaan berlanjut kembali melalui Pemilihan Tindakan, Middleware Perutean, dan berbagai Middleware Lainnya sebelum menjadi respons yang dikirim ke klien.

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.
    • Hentikan proses jika permintaan tidak terotorisasi.
  • Filter sumber daya:

    • Jalankan setelah otorisasi.
    • OnResourceExecuting menjalankan kode sebelum alur pemrosesan filter lainnya mulai. Misalnya, OnResourceExecuting menjalankan kode sebelum pengikatan model.
    • OnResourceExecuted menjalankan program setelah tahap lain dari alur selesai.
  • Filter aksi:

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

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

  • Filter hasil:

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

Diagram berikut menunjukkan bagaimana jenis filter berinteraksi dalam alur filter:

Permintaan diproses melalui Filter Otorisasi, Filter Sumber Daya, Pengikatan Model, Filter Tindakan, Eksekusi Tindakan dan Konversi Hasil Tindakan, Filter Pengecualian, Filter Hasil, dan Eksekusi Hasil. Saat keluar, permintaan hanya diproses oleh Filter Hasil dan Filter Sumber Daya sebelum menjadi respons yang dikirim ke klien.

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 aksi mengembalikan hasil:

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 metode On-Stage-ExecutionAsync. 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 ResponseHeaderAttribute ke pengontrol atau metode tindakan dan tentukan nama serta 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 Response Headers, 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.");
}

Respon dari tindakan Multiple mencakup tajuk 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.

Filter Atribut:

Filter tidak dapat diterapkan ke metode handler halaman Razor. Mereka dapat diterapkan baik secara global maupun ke Model Halaman Razor.

Cakupan penyaringan 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 kerja, cakupan menentukan urutan default dari eksekusi filter. Filter global mengelilingi filter kelas, yang pada gilirannya mengelilingi filter metode.

Akibat dari penyaringan bersarang, kode setelah filter berjalan dalam urutan terbalik dari kode sebelum. Urutan filter:

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

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

Urutan Cakupan filter Metode filter
1 Mendunia OnActionExecuting
2 Pengontrol OnActionExecuting
3 Perbuatan OnActionExecuting
4 Perbuatan OnActionExecuted
5 Pengontrol OnActionExecuted
6 Mendunia OnActionExecuted

Filter pada tingkat pengendali

Setiap pengontrol yang mewarisi dari Controller mencakup metode OnActionExecuting, OnActionExecutionAsync, dan OnActionExecuted. Metode ini membungkus filter yang dijalankan untuk tindakan tertentu.

  • OnActionExecuting berjalan sebelum filter-filter tindakan apa pun.
  • OnActionExecuted berjalan setelah semua filter tindakan.
  • OnActionExecutionAsync berjalan sebelum filter-filter tindakan apa pun. Kode setelah pemanggilan ke next berjalan setelah filter tindakan.

Kelas berikut ControllerFiltersController :

  • Menerapkan SampleActionFilterAttribute ([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 ke 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 yang diterapkan pada metode. Pesanan dijelaskan di bagian berikutnya.

Untuk Razor Halaman, lihat Razor.

Mengatasi urutan default

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

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

Dalam contoh filter tingkat pengontrol, memiliki cakupan global sehingga berjalan sebelum GlobalSampleActionFilter, 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 Pemutusan Arus Pendek

Alur filter dapat diputus dengan mengatur properti Result pada parameter yang disediakan oleh ResourceExecutingContext 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, baik filter [ShortCircuitingResourceFilter] maupun [ResponseHeader] menargetkan metode tindakan Index. Filter ShortCircuitingResourceFilterAttribute :

  • Berjalan terlebih dahulu, karena ini adalah Filter Sumber Daya dan ResponseHeaderAttribute merupakan Filter Tindakan.
  • Menghentikan sisa proses.

Oleh karena itu, filter ResponseHeaderAttribute tidak pernah berjalan untuk tindakan Index. Perilaku ini akan sama jika kedua filter diterapkan pada tingkat metode tindakan, asalkan ShortCircuitingResourceFilterAttribute dijalankan terlebih dahulu. ShortCircuitingResourceFilterAttribute berjalan lebih dahulu 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 sebuah tipe ditambahkan, maka tipe tersebut akan diaktifkan. Filter yang diaktifkan berdasarkan 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.

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

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

Atribut Penyaring Layanan

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, atribut ServiceFilter mengambil contoh filter LoggingResponseHeaderFilterService dari DI.

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

Saat menggunakan ServiceFilterAttribute, menyetel ServiceFilterAttribute.IsReusable:

  • Memberikan indikasi bahwa instans filter dapat digunakan kembali di luar cakupan permintaan tempat pembuatannya. 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 yang berbeda dari singleton.

ServiceFilterAttribute menerapkan IFilterFactory. IFilterFactory mengekspos metode CreateInstance untuk membuat instans IFilterMetadata. CreateInstance memuat tipe yang ditentukan dari DI.

TypeFilterAttribute

TypeFilterAttribute mirip dengan ServiceFilterAttribute, tetapi tipe TypeFilterAttribute tidak diambil langsung dari kontainer DI. Ini menginstansiasi 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, menyetel TypeFilterAttribute.IsReusable:

  • Memberikan indikasi bahwa instans filter dapat digunakan kembali di luar cakupan permintaan asal pembuatannya. 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 'before', tetapi tidak memiliki metode 'after'.

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 melempar 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:

Pemfilteran sumber daya berguna untuk memotong sebagian besar alur. Misalnya, filter penyimpanan sementara dapat menghindari sisa proses saat terjadi hit cache.

Contoh filter sumber daya:

Penyaring 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.
    }
}

ActionExecutingContext menyediakan properti berikut:

  • ActionArguments - memungkinkan untuk membaca masukan ke dalam 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 penerapan filter berikutnya.
  • Tidak seperti pengaturan Result, ini diperlakukan sebagai kegagalan, bukan hasil sukses.

menyediakan ActionExecutedContextController dan Result ditambah properti berikut:

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

Untuk IAsyncActionFilter, panggilan ke ActionExecutionDelegate:

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

Untuk memutus rangkaian, tetapkan Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result ke instance hasil dan jangan panggil next (ActionExecutionDelegate).

Kerangka kerja menyediakan abstrak ActionFilterAttribute yang dapat disubkelas.

OnActionExecuting Filter tindakan dapat digunakan untuk:

  • Validasi 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 dijalankan setelah metode aksi:

  • Dan dapat melihat dan memanipulasi hasil tindakan melalui Result properti .
  • Canceled diatur ke true jika eksekusi tindakan dibatalkan 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 hasilnya dikembalikan secara normal dari metode tersebut.

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 tidak tertangani yang terjadi saat pembuatan Razor halaman atau pengontrol, pengikatan model, filter tindakan, atau metode tindakan.
  • Jangan menangkap pengecualian yang terjadi pada filter sumber daya, filter hasil, atau eksekusi hasil MVC.

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

Filter pengecualian:

  • Baik untuk menangkap pengecualian yang terjadi dalam aksi.
  • 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 JSON, 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.
    }
}

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

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

  • Filter otorisasi atau filter sumber daya memotong alur proses.
  • Filter pengecualian menangani pengecualian dengan menghasilkan hasil tindakan.

Metode Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting dapat menghentikan eksekusi lebih awal 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 menjadi true jika eksekusi hasil aksi dihentikan oleh filter lain.

ResultExecutedContext.Exception disetel ke dalam nilai non-null jika hasil tindakan atau filter hasil berikutnya memunculkan 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 sudah dikirim ke klien ketika hasil tindakan menghasilkan pengecualian, tidak ada mekanisme yang dapat diandalkan untuk mengirimkan kode kegagalan.

Untuk IAsyncResultFilter, pemanggilan await next pada ResultExecutionDelegate menjalankan penyaring 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 menghentikan proses di tengah jalan.
  • 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 menerapkan IFilterMetadata. Oleh karena itu, IFilterFactory instans dapat digunakan sebagai IFilterMetadata instans di mana saja dalam alur filter. Ketika runtime bersiap untuk memanggil filter, runtime berusaha mengonversinya 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 fabrik bahwa instance filter yang dibuat oleh fabrik dapat digunakan kembali di luar lingkup permintaan tempat instance tersebut dibuat.
  • Jangan digunakan dengan filter yang bergantung pada layanan dengan masa pakai yang berbeda dari singleton.

ASP.NET Core runtime 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 bersifat stateless, dan filter aman digunakan di berbagai 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 mengimplementasikan IFilterFactory berguna untuk filter yang:

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

TypeFilterAttribute menerapkan IFilterFactory. IFilterFactory mengekspos metode CreateInstance untuk membuat instans IFilterMetadata. CreateInstance memuat tipe yang ditentukan dari wadah 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.

Gunakan 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 tipe dengan metode Configure yang menentukan middleware untuk dimasukkan ke dalam rangkaian 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 Thread

Saat meneruskan instans dari filter ke Add, bukan ke Type, filter tersebut menjadi singleton dan tidak aman dalam konteks multi-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, memotong jalur permintaan untuk mengembalikan respons yang telah di-cache.

Filter kustom dapat dibuat untuk menangani kekhawatiran lintas fungsi. Contoh concern lintas proses termasuk penanganan kesalahan, penyimpanan sementara, konfigurasi, otorisasi, dan pencatatan. 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, pengontrol, dan tampilan menggunakan filter.

Lihat atau unduh sampel (cara mengunduh).

Cara kerja filter

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

Permintaan diproses melalui Middleware Lainnya, Middleware Perutean, Pemilihan Tindakan, dan Alur Pemanggilan Tindakan. Pemrosesan permintaan berlanjut kembali melalui Pemilihan Tindakan, Middleware Perutean, dan berbagai Middleware Lainnya sebelum menjadi respons yang dikirim ke klien.

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. Filter otorisasi menghentikan alur proses jika permintaan tidak diizinkan.

  • Filter sumber daya:

    • Jalankan setelah otorisasi.
    • OnResourceExecuting menjalankan kode sebelum alur pemrosesan filter lainnya mulai. Misalnya, OnResourceExecuting menjalankan kode sebelum pengikatan model.
    • OnResourceExecuted menjalankan program setelah tahap lain dari alur selesai.
  • Filter aksi:

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

  • Filter hasil menjalankan kode segera sebelum dan sesudah eksekusi hasil tindakan. Proses ini hanya dijalankan ketika metode tindakan berhasil dieksekusi. Ini berguna untuk logika yang harus mendukung tampilan atau pemrosesan format.

Diagram berikut menunjukkan bagaimana jenis filter berinteraksi dalam alur filter.

Permintaan diproses melalui Filter Otorisasi, Filter Sumber Daya, Pengikatan Model, Filter Tindakan, Eksekusi Tindakan dan Konversi Hasil Tindakan, Filter Pengecualian, Filter Hasil, dan Eksekusi Hasil. Saat keluar, permintaan hanya diproses oleh Filter Hasil dan Filter Sumber Daya sebelum menjadi respons yang dikirim ke klien.

implementasi

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

Filter sinkron menjalankan kode sebelum dan sesudah tahap jalur pemrosesan mereka. Misalnya, OnActionExecuting dipanggil sebelum metode tindakan dipanggil. OnActionExecuted dipanggil setelah metode aksi mengembalikan hasil.

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 metode On-Stage-ExecutionAsync. 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 AddHeaderAttribute ke pengontrol atau metode tindakan dan tentukan nama serta 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 Response Headers, author: Rick Anderson ditampilkan.

Kode berikut mengimplementasikan ActionFilterAttribute yang:

  • Membaca judul dan nama dari sistem konfigurasi tersebut. 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 pengaturan. 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 telah ditambahkan ke wadah layanan.
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(
             Configuration.GetSection("Position"));
    services.AddScoped<MyActionFilterAttribute>();

    services.AddControllersWithViews();
}

Kode berikut menunjukkan kelas PositionOptions.

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

Kode berikut menerapkan MyActionFilterAttribute ke metode Index2:

[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 Respons Header, author: Rick Anderson, dan Editor: Joe Smith akan ditampilkan saat Sample/Index2 titik akhir dipanggil.

Kode berikut menerapkan MyActionFilterAttribute dan AddHeaderAttribute ke Halaman Razor.

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

Filter tidak dapat diterapkan ke metode handler halaman Razor. Mereka dapat diterapkan baik secara global maupun ke Model Halaman Razor.

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

Filter Atribut:

Cakupan penyaringan 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 kerja, cakupan menentukan urutan default dari eksekusi filter. Filter global mengelilingi filter kelas, yang pada gilirannya mengelilingi filter metode.

Akibat dari penyaringan bersarang, kode setelah filter berjalan dalam urutan terbalik dari kode sebelum. Urutan filter:

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

Contoh berikut ini menggambarkan urutan dimana metode filter dijalankan pada filter tindakan sinkron.

Urutan Cakupan filter Metode filter
1 Mendunia OnActionExecuting
2 Kontroler atau Razor Halaman OnActionExecuting
3 Metode OnActionExecuting
4 Metode OnActionExecuted
5 Kontroler atau Razor Halaman OnActionExecuted
6 Mendunia OnActionExecuted

Filter pada tingkat pengendali

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

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

Misalnya, dalam sampel unduhan, MySampleActionFilter diterapkan secara global saat memulai.

TestController:

  • Menerapkan SampleActionFilterAttribute ([SampleActionFilter]) pada tindakan FilterTest2.
  • 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 ke 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 yang diterapkan pada metode. Pesanan dijelaskan di bagian berikutnya.

Untuk Razor Halaman, lihat Razor.

Mengesampingkan urutan default

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

  • Menjalankan kode sebelum yang dimiliki oleh filter dengan nilai yang lebih tinggi dari Order.
  • Menjalankan kode setelah kode 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 menetapkan properti Pesanan ke int.MinValue Untuk filter bawaan, cakupan yang menentukan pesanan 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 urutannya 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 Pemutusan Arus Pendek

Alur filter dapat diputus dengan mengatur properti Result pada parameter yang disediakan oleh ResourceExecutingContext 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, baik filter ShortCircuitingResourceFilter maupun AddHeader menargetkan metode tindakan SomeResource. ShortCircuitingResourceFilter:

  • Berjalan terlebih dahulu, karena ini adalah Filter Sumber Daya dan AddHeader merupakan Filter Tindakan.
  • Menghentikan sisa proses.

Oleh karena itu, filter AddHeader tidak pernah berjalan untuk tindakan SomeResource. Perilaku ini akan sama jika kedua filter diterapkan pada tingkat metode tindakan, asalkan ShortCircuitingResourceFilter dijalankan terlebih dahulu. ShortCircuitingResourceFilter berjalan dulu karena jenis filternya, atau dengan penggunaan properti Order 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 sebuah tipe ditambahkan, maka tipe tersebut akan diaktifkan. Filter yang diaktifkan berdasarkan 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:

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

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

Atribut Penyaring Layanan

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, atribut ServiceFilter mengambil contoh filter AddHeaderResultServiceFilter dari DI.

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

Saat menggunakan ServiceFilterAttribute, menyetel ServiceFilterAttribute.IsReusable:

  • Memberikan indikasi bahwa instans filter dapat digunakan kembali di luar cakupan permintaan tempat pembuatannya. ASP.NET Core runtime 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 menerapkan IFilterFactory. IFilterFactory mengekspos metode CreateInstance untuk membuat instans IFilterMetadata. CreateInstance memuat tipe yang ditentukan dari DI.

TypeFilterAttribute

TypeFilterAttribute mirip dengan ServiceFilterAttribute, tetapi tipe TypeFilterAttribute tidak diambil langsung dari kontainer DI. Ini menginstansiasi 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, menyetel TypeFilterAttribute.IsReusable:

  • Memberikan indikasi bahwa instans filter dapat digunakan kembali di luar cakupan permintaan asal pembuatannya. 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 'before', tetapi tidak memiliki metode 'after'.

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 melempar 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:

Pemfilteran sumber daya berguna untuk memotong sebagian besar alur. Misalnya, filter penyimpanan sementara dapat menghindari sisa proses saat terjadi hit cache.

Contoh filter sumber daya:

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

ActionExecutingContext menyediakan properti berikut:

  • ActionArguments - memungkinkan untuk membaca masukan ke dalam 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 penerapan filter berikutnya.
  • Tidak seperti pengaturan Result, ini diperlakukan sebagai kegagalan, bukan hasil sukses.

menyediakan ActionExecutedContextController dan Result ditambah properti berikut:

  • Canceled - Bernilai benar jika eksekusi tindakan dihentikan oleh filter lain.

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

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

Untuk IAsyncActionFilter, panggilan ke ActionExecutionDelegate:

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

Untuk memutus rangkaian, tetapkan Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result ke instance hasil dan jangan panggil next (ActionExecutionDelegate).

Kerangka kerja menyediakan abstrak ActionFilterAttribute yang dapat disubkelas.

OnActionExecuting Filter tindakan dapat digunakan untuk:

  • Validasi 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 dijalankan setelah metode aksi:

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

  • Canceled diatur ke true jika eksekusi tindakan dibatalkan 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 hasilnya dikembalikan secara normal dari metode tersebut.
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 tidak tertangani yang terjadi saat pembuatan Razor halaman atau pengontrol, pengikatan model, filter tindakan, atau metode tindakan.
  • Jangan menangkap pengecualian yang terjadi pada filter sumber daya, filter hasil, atau eksekusi hasil MVC.

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

Filter pengecualian:

  • Baik untuk menangkap pengecualian yang terjadi dalam aksi.
  • 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 JSON, 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");
    }
}

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

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

  • Filter otorisasi atau filter sumber daya memotong alur proses.
  • Filter pengecualian menangani pengecualian dengan menghasilkan hasil tindakan.

Metode Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting dapat menghentikan eksekusi lebih awal 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 menjadi true jika eksekusi hasil aksi dihentikan oleh filter lain.

ResultExecutedContext.Exception disetel ke dalam nilai non-null jika hasil tindakan atau filter hasil berikutnya memunculkan 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 sudah dikirim ke klien ketika hasil tindakan menghasilkan pengecualian, tidak ada mekanisme yang dapat diandalkan untuk mengirimkan kode kegagalan.

Untuk IAsyncResultFilter, pemanggilan await next pada ResultExecutionDelegate menjalankan penyaring 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 menghentikan proses di tengah jalan.
  • 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 menerapkan IFilterMetadata. Oleh karena itu, IFilterFactory instans dapat digunakan sebagai IFilterMetadata instans di mana saja dalam alur filter. Ketika runtime bersiap untuk memanggil filter, runtime berusaha mengonversinya 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 fabrik bahwa instance filter yang dibuat oleh fabrik dapat digunakan kembali di luar lingkup permintaan tempat instance tersebut dibuat.
  • Jangan digunakan dengan filter yang bergantung pada layanan dengan masa pakai yang berbeda dari singleton.

ASP.NET Core runtime 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 bersifat stateless, dan filter aman digunakan di berbagai 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.
  • Akses https://localhost:5001/Sample/HeaderWithFactory.

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

  • pengarang:Rick Anderson
  • globaladdheader:Result filter added to MvcOptions.Filters
  • intern:My header

Kode sebelumnya membuat header internal:My header respons.

IFilterFactory diimplementasikan pada atribut

Filter yang mengimplementasikan IFilterFactory berguna untuk filter yang:

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

TypeFilterAttribute menerapkan IFilterFactory. IFilterFactory mengekspos metode CreateInstance untuk membuat instans IFilterMetadata. CreateInstance memuat tipe yang ditentukan dari wadah 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 pada jalur 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 tipe dengan metode Configure yang menentukan middleware untuk dimasukkan ke dalam rangkaian 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 Thread

Saat meneruskan instans dari filter ke Add, bukan ke Type, filter tersebut menjadi singleton dan tidak aman dalam konteks multi-utas.

Tindakan berikutnya