Menangani kesalahan di ASP.NET Core

Catatan

Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Penting

Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.

Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Oleh Tom Dykstra

Artikel ini membahas pendekatan umum untuk menangani kesalahan di aplikasi web ASP.NET Core. Lihat juga Menangani kesalahan di API web ASP.NET Core dan Menangani kesalahan di aplikasi API Minimal.

Halaman pengecualian pengembang

Halaman Pengecualian Pengembang menampilkan informasi terperinci tentang pengecualian permintaan yang tidak tertangani. aplikasi ASP.NET Core mengaktifkan halaman pengecualian pengembang secara default saat keduanya:

Halaman pengecualian pengembang berjalan lebih awal di alur middleware, sehingga dapat menangkap pengecualian yang tidak tertangani yang dilemparkan dalam middleware yang mengikuti.

Informasi pengecualian terperinci tidak boleh ditampilkan secara publik saat aplikasi berjalan di lingkungan Produksi. Untuk informasi selengkapnya tentang mengonfigurasi lingkungan, lihat Menggunakan beberapa lingkungan di ASP.NET Core.

Halaman Pengecualian Pengembang dapat menyertakan informasi berikut tentang pengecualian dan permintaan:

  • Pelacakan tumpukan
  • Parameter string kueri, jika ada
  • Cookies, jika ada
  • Header
  • Metadata titik akhir, jika ada

Gambar berikut menunjukkan halaman pengecualian pengembang sampel dengan Metadata perutean dipilih dan titik akhir ditampilkan:

Halaman pengecualian pengembang dengan Perutean dipilih dan metadata titik akhir ditampilkan

Halaman Pengecualian Pengembang tidak dijamin untuk memberikan informasi apa pun. Gunakan Pengelogan untuk informasi kesalahan lengkap.

Halaman handler pengecualian

Untuk mengonfigurasi halaman penanganan kesalahan kustom untuk lingkungan Produksi, panggil UseExceptionHandler. Middleware penanganan pengecualian ini:

  • Menangkap dan mencatat pengecualian yang tidak tertangani.
  • Menjalankan kembali permintaan dalam alur alternatif menggunakan jalur yang ditunjukkan. Permintaan tidak dijalankan kembali jika respons telah dimulai. Kode yang dihasilkan templat menjalankan kembali permintaan menggunakan /Error jalur .

Peringatan

Jika alur alternatif melemparkan pengecualian sendiri, Exception Handling Middleware akan memunculkan kembali pengecualian asli.

Karena middleware ini dapat menjalankan kembali alur permintaan:

  • Middleware perlu menangani reentrancy dengan permintaan yang sama. Ini biasanya berarti membersihkan status mereka setelah memanggil _next atau menyimpan cache pemrosesan mereka untuk HttpContext menghindari mengulanginya. Saat berhadapan dengan isi permintaan, ini berarti buffering atau penembolokan hasil seperti pembaca Formulir.
  • UseExceptionHandler(IApplicationBuilder, String) Untuk kelebihan beban yang digunakan dalam templat, hanya jalur permintaan yang dimodifikasi, dan data rute dibersihkan. Data permintaan seperti header, metode, dan item semuanya digunakan kembali apa adanya.
  • Layanan terlingkup tetap sama.

Dalam contoh berikut, UseExceptionHandler menambahkan middleware penanganan pengecualian di lingkungan non-Pengembangan:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

Razor Templat aplikasi Halaman menyediakan halaman Kesalahan (.cshtml) dan PageModel kelas (ErrorModel) di folder Halaman. Untuk aplikasi MVC, templat proyek menyertakan Error metode tindakan dan tampilan Kesalahan untuk Home pengontrol.

Middleware penanganan pengecualian menjalankan kembali permintaan menggunakan metode HTTP asli . Jika titik akhir handler kesalahan dibatasi untuk sekumpulan metode HTTP tertentu, titik akhir hanya berjalan untuk metode HTTP tersebut. Misalnya, tindakan pengontrol MVC yang menggunakan [HttpGet] atribut hanya berjalan untuk permintaan GET. Untuk memastikan bahwa semua permintaan mencapai halaman penanganan kesalahan kustom, jangan batasi ke sekumpulan metode HTTP tertentu.

Untuk menangani pengecualian secara berbeda berdasarkan metode HTTP asli:

  • Untuk Razor Pages, buat beberapa metode handler. Misalnya, gunakan OnGet untuk menangani pengecualian GET dan gunakan OnPost untuk menangani pengecualian POST.
  • Untuk MVC, terapkan atribut kata kerja HTTP ke beberapa tindakan. Misalnya, gunakan [HttpGet] untuk menangani pengecualian GET dan gunakan [HttpPost] untuk menangani pengecualian POST.

Untuk mengizinkan pengguna yang tidak diaturentikasi untuk melihat halaman penanganan kesalahan kustom, pastikan pengguna tersebut mendukung akses anonim.

Mengakses pengecualian

Gunakan IExceptionHandlerPathFeature untuk mengakses pengecualian dan jalur permintaan asli dalam penangan kesalahan. Contoh berikut menggunakan IExceptionHandlerPathFeature untuk mendapatkan informasi selengkapnya tentang pengecualian yang dilemparkan:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
    public string? RequestId { get; set; }

    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    public string? ExceptionMessage { get; set; }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        var exceptionHandlerPathFeature =
            HttpContext.Features.Get<IExceptionHandlerPathFeature>();

        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            ExceptionMessage = "The file was not found.";
        }

        if (exceptionHandlerPathFeature?.Path == "/")
        {
            ExceptionMessage ??= string.Empty;
            ExceptionMessage += " Page: Home.";
        }
    }
}

Peringatan

Jangan melayani informasi kesalahan sensitif kepada klien. Melayani kesalahan adalah risiko keamanan.

Penangan pengecualian lambda

Alternatif untuk halaman handler pengecualian kustom adalah menyediakan lambda ke UseExceptionHandler. Menggunakan lambda memungkinkan akses ke kesalahan sebelum mengembalikan respons.

Kode berikut menggunakan lambda untuk penanganan pengecualian:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            // using static System.Net.Mime.MediaTypeNames;
            context.Response.ContentType = Text.Plain;

            await context.Response.WriteAsync("An exception was thrown.");

            var exceptionHandlerPathFeature =
                context.Features.Get<IExceptionHandlerPathFeature>();

            if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
            {
                await context.Response.WriteAsync(" The file was not found.");
            }

            if (exceptionHandlerPathFeature?.Path == "/")
            {
                await context.Response.WriteAsync(" Page: Home.");
            }
        });
    });

    app.UseHsts();
}

Peringatan

Jangan melayani informasi kesalahan sensitif kepada klien. Melayani kesalahan adalah risiko keamanan.

IExceptionHandler

IExceptionHandler adalah antarmuka yang memberi pengembang panggilan balik untuk menangani pengecualian yang diketahui di lokasi pusat.

IExceptionHandler implementasi didaftarkan dengan memanggil IServiceCollection.AddExceptionHandler<T>. Masa pakai IExceptionHandler instans adalah singleton. Beberapa implementasi dapat ditambahkan, dan dipanggil dalam urutan terdaftar.

Jika handler pengecualian menangani permintaan, handler tersebut dapat kembali true berhenti memproses. Jika pengecualian tidak ditangani oleh handler pengecualian apa pun, kontrol akan kembali ke perilaku dan opsi default dari middleware. Metrik dan log yang berbeda dipancarkan untuk pengecualian yang ditangani versus pengecualian yang tidak tertangani.

Contoh berikut menunjukkan IExceptionHandler implementasi:

using Microsoft.AspNetCore.Diagnostics;

namespace ErrorHandlingSample
{
    public class CustomExceptionHandler : IExceptionHandler
    {
        private readonly ILogger<CustomExceptionHandler> logger;
        public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
        {
            this.logger = logger;
        }
        public ValueTask<bool> TryHandleAsync(
            HttpContext httpContext,
            Exception exception,
            CancellationToken cancellationToken)
        {
            var exceptionMessage = exception.Message;
            logger.LogError(
                "Error Message: {exceptionMessage}, Time of occurrence {time}",
                exceptionMessage, DateTime.UtcNow);
            // Return false to continue with the default behavior
            // - or - return true to signal that this exception is handled
            return ValueTask.FromResult(false);
        }
    }
}

Contoh berikut menunjukkan cara mendaftarkan IExceptionHandler implementasi untuk injeksi dependensi:

using ErrorHandlingSample;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
builder.Services.AddExceptionHandler<CustomExceptionHandler>();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

// Remaining Program.cs code omitted for brevity

Saat kode sebelumnya berjalan di lingkungan Pengembangan:

  • dipanggil CustomExceptionHandler terlebih dahulu untuk menangani pengecualian.
  • Setelah mencatat pengecualian, TryHandleException metode mengembalikan false, sehingga halaman pengecualian pengembang ditampilkan.

Di lingkungan lain:

  • dipanggil CustomExceptionHandler terlebih dahulu untuk menangani pengecualian.
  • Setelah mencatat pengecualian, TryHandleException metode mengembalikan false, sehingga /Error halaman ditampilkan.

GunakanStatusCodePages

Secara default, aplikasi ASP.NET Core tidak menyediakan halaman kode status untuk kode status kesalahan HTTP, seperti 404 - Tidak Ditemukan. Saat aplikasi menetapkan kode status kesalahan HTTP 400-599 yang tidak memiliki isi, aplikasi mengembalikan kode status dan isi respons kosong. Untuk mengaktifkan penangan teks-saja default untuk kode status kesalahan umum, panggil UseStatusCodePages di Program.cs:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages();

Panggil UseStatusCodePages sebelum meminta penanganan middleware. Misalnya, panggil UseStatusCodePages sebelum Middleware File Statis dan Middleware Titik Akhir.

Saat UseStatusCodePages tidak digunakan, menavigasi ke URL tanpa titik akhir mengembalikan pesan kesalahan yang bergantung pada browser yang menunjukkan titik akhir tidak dapat ditemukan. Ketika UseStatusCodePages dipanggil, browser mengembalikan respons berikut:

Status Code: 404; Not Found

UseStatusCodePages biasanya tidak digunakan dalam produksi karena mengembalikan pesan yang tidak berguna bagi pengguna.

Catatan

Halaman kode status middleware tidak menangkap pengecualian. Untuk menyediakan halaman penanganan kesalahan kustom, gunakan halaman handler pengecualian.

GunakanStatusCodePages dengan string format

Untuk mengkustomisasi tipe konten respons dan teks, gunakan kelebihan beban UseStatusCodePages yang mengambil tipe konten dan format string:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

// using static System.Net.Mime.MediaTypeNames;
app.UseStatusCodePages(Text.Plain, "Status Code Page: {0}");

Dalam kode sebelumnya, {0} adalah tempat penampung untuk kode kesalahan.

UseStatusCodePages dengan string format biasanya tidak digunakan dalam produksi karena menampilkan pesan yang tidak berguna bagi pengguna.

GunakanStatusCodePages dengan lambda

Untuk menentukan penanganan kesalahan kustom dan kode penulisan respons, gunakan kelebihan beban UseStatusCodePages yang mengambil ekspresi lambda:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages(async statusCodeContext =>
{
    // using static System.Net.Mime.MediaTypeNames;
    statusCodeContext.HttpContext.Response.ContentType = Text.Plain;

    await statusCodeContext.HttpContext.Response.WriteAsync(
        $"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});

UseStatusCodePages dengan lambda biasanya tidak digunakan dalam produksi karena mengembalikan pesan yang tidak berguna bagi pengguna.

GunakanStatusCodePagesWithRedirects

Metode UseStatusCodePagesWithRedirects ekstensi:

  • Mengirim kode status 302 - Ditemukan ke klien.
  • Mengalihkan klien ke titik akhir penanganan kesalahan yang disediakan dalam templat URL. Titik akhir penanganan kesalahan biasanya menampilkan informasi kesalahan dan mengembalikan HTTP 200.
var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");

Templat URL dapat menyertakan {0} tempat penampung untuk kode status, seperti yang ditunjukkan dalam kode sebelumnya. Jika templat URL dimulai dengan ~ (tilde), ~ digantikan oleh aplikasi PathBase. Saat menentukan titik akhir di aplikasi, buat tampilan atau Razor halaman MVC untuk titik akhir.

Metode ini umumnya digunakan saat aplikasi:

  • Harus mengalihkan klien ke titik akhir yang berbeda, biasanya dalam kasus di mana aplikasi yang berbeda memproses kesalahan. Untuk aplikasi web, bilah alamat browser klien mencerminkan titik akhir yang dialihkan.
  • Tidak boleh mempertahankan dan mengembalikan kode status asli dengan respons pengalihan awal.

GunakanStatusCodePagesWithReExecute

Metode UseStatusCodePagesWithReExecute ekstensi:

  • Menghasilkan isi respons dengan mengeksekusi ulang alur permintaan menggunakan jalur alternatif.
  • Tidak mengubah kode status sebelum atau sesudah mengeksekusi ulang alur.

Eksekusi alur baru dapat mengubah kode status respons, karena alur baru memiliki kontrol penuh atas kode status. Jika alur baru tidak mengubah kode status, kode status asli akan dikirim ke klien.

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");

Jika titik akhir dalam aplikasi ditentukan, buat tampilan atau Razor halaman MVC untuk titik akhir.

Metode ini umumnya digunakan ketika aplikasi harus:

  • Proses permintaan tanpa mengalihkan ke titik akhir yang berbeda. Untuk aplikasi web, bilah alamat browser klien mencerminkan titik akhir yang awalnya diminta.
  • Pertahankan dan kembalikan kode status asli dengan respons.

Templat URL harus dimulai dengan / dan dapat menyertakan tempat penampung {0} untuk kode status. Untuk meneruskan kode status sebagai parameter string kueri, teruskan argumen kedua ke dalam UseStatusCodePagesWithReExecute. Contohnya:

var app = builder.Build();  
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");

Titik akhir yang memproses kesalahan bisa mendapatkan URL asli yang menghasilkan kesalahan, seperti yang ditunjukkan dalam contoh berikut:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class StatusCodeModel : PageModel
{
    public int OriginalStatusCode { get; set; }

    public string? OriginalPathAndQuery { get; set; }

    public void OnGet(int statusCode)
    {
        OriginalStatusCode = statusCode;

        var statusCodeReExecuteFeature =
            HttpContext.Features.Get<IStatusCodeReExecuteFeature>();

        if (statusCodeReExecuteFeature is not null)
        {
            OriginalPathAndQuery = string.Join(
                statusCodeReExecuteFeature.OriginalPathBase,
                statusCodeReExecuteFeature.OriginalPath,
                statusCodeReExecuteFeature.OriginalQueryString);
        }
    }
}

Karena middleware ini dapat menjalankan kembali alur permintaan:

  • Middleware perlu menangani reentrancy dengan permintaan yang sama. Ini biasanya berarti membersihkan status mereka setelah memanggil _next atau menyimpan cache pemrosesan mereka untuk HttpContext menghindari mengulanginya. Saat berhadapan dengan isi permintaan, ini berarti buffering atau penembolokan hasil seperti pembaca Formulir.
  • Layanan terlingkup tetap sama.

Menonaktifkan halaman kode status

Untuk menonaktifkan halaman kode status untuk pengontrol MVC atau metode tindakan, gunakan atribut [SkipStatusCodePages ].

Untuk menonaktifkan halaman kode status untuk permintaan tertentu dalam Razor metode handler Pages atau di pengontrol MVC, gunakan IStatusCodePagesFeature:

public void OnGet()
{
    var statusCodePagesFeature =
        HttpContext.Features.Get<IStatusCodePagesFeature>();

    if (statusCodePagesFeature is not null)
    {
        statusCodePagesFeature.Enabled = false;
    }
}

Kode penanganan pengecualian

Kode dalam halaman penanganan pengecualian juga dapat melemparkan pengecualian. Halaman kesalahan produksi harus diuji secara menyeluruh dan berhati-hatilah untuk menghindari melempar pengecualian sendiri.

Header respons

Setelah header untuk respons dikirim:

  • Aplikasi tidak dapat mengubah kode status respons.
  • Halaman pengecualian atau handler apa pun tidak dapat dijalankan. Respons harus diselesaikan atau koneksi dibatalkan.

Penanganan pengecualian server

Selain logika penanganan pengecualian dalam aplikasi, implementasi server HTTP dapat menangani beberapa pengecualian. Jika server menangkap pengecualian sebelum header respons dikirim, server mengirim 500 - Internal Server Error respons tanpa isi respons. Jika server menangkap pengecualian setelah header respons dikirim, server menutup koneksi. Permintaan yang tidak ditangani oleh aplikasi ditangani oleh server. Pengecualian apa pun yang terjadi ketika server menangani permintaan ditangani oleh penanganan pengecualian server. Halaman kesalahan kustom aplikasi, middleware penanganan pengecualian, dan filter tidak memengaruhi perilaku ini.

Penanganan pengecualian startup

Hanya lapisan hosting yang dapat menangani pengecualian yang terjadi selama startup aplikasi. Host dapat dikonfigurasi untuk menangkap kesalahan startup dan menangkap kesalahan terperinci.

Lapisan hosting dapat menampilkan halaman kesalahan untuk kesalahan startup yang diambil hanya jika kesalahan terjadi setelah alamat host/pengikatan port. Jika pengikatan gagal:

  • Lapisan hosting mencatat pengecualian penting.
  • Proses dotnet mengalami crash.
  • Tidak ada halaman kesalahan yang ditampilkan ketika server HTTP adalah Kestrel.

Saat berjalan di IIS (atau Azure App Service) atau IIS Express, 502.5 - Kegagalan Proses dikembalikan oleh Modul Inti ASP.NET jika proses tidak dapat dimulai. Untuk informasi selengkapnya, lihat Memecahkan masalah ASP.NET Core di Azure App Service dan IIS.

Halaman kesalahan database

Filter AddDatabaseDeveloperPageExceptionFilter pengecualian halaman pengembang Database menangkap pengecualian terkait database yang dapat diselesaikan dengan menggunakan migrasi Entity Framework Core. Ketika pengecualian ini terjadi, respons HTML dihasilkan dengan detail kemungkinan tindakan untuk menyelesaikan masalah. Halaman ini hanya diaktifkan di lingkungan Pengembangan. Kode berikut menambahkan filter pengecualian halaman pengembang Database:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();

Filter pengecualian

Di aplikasi MVC, filter pengecualian dapat dikonfigurasi secara global atau berdasarkan per pengontrol atau per tindakan. Di Razor aplikasi Pages, aplikasi tersebut dapat dikonfigurasi secara global atau per model halaman. Filter ini menangani pengecualian yang tidak tertangani yang terjadi selama eksekusi tindakan pengontrol atau filter lain. Untuk informasi selengkapnya, lihat Filter di ASP.NET Core.

Filter pengecualian berguna untuk menjebak pengecualian yang terjadi dalam tindakan MVC, tetapi tidak fleksibel seperti middleware penanganan pengecualian bawaan, UseExceptionHandler. Sebaiknya gunakan UseExceptionHandler, kecuali Anda perlu melakukan penanganan kesalahan secara berbeda berdasarkan tindakan MVC mana yang dipilih.

Kesalahan status model

Untuk informasi tentang cara menangani kesalahan status model, lihat Pengikatan model dan Validasi model.

Detail masalah

Detail Masalah bukan satu-satunya format respons untuk menjelaskan kesalahan API HTTP, namun, biasanya digunakan untuk melaporkan kesalahan untuk API HTTP.

Layanan detail masalah mengimplementasikan IProblemDetailsService antarmuka, yang mendukung pembuatan detail masalah di ASP.NET Core. Metode AddProblemDetails ekstensi pada IServiceCollection mendaftarkan implementasi default IProblemDetailsService .

Di aplikasi ASP.NET Core, middleware berikut menghasilkan respons HTTP detail masalah saat AddProblemDetails dipanggil, kecuali ketika Accept header HTTP permintaan tidak menyertakan salah satu jenis konten yang didukung oleh yang terdaftar IProblemDetailsWriter (default: application/json):

Kode berikut mengonfigurasi aplikasi untuk menghasilkan respons detail masalah untuk semua respons kesalahan klien HTTP dan server yang belum memiliki konten isi:

builder.Services.AddProblemDetails();

var app = builder.Build();        

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
    app.UseHsts();
}

app.UseStatusCodePages();

Bagian berikutnya memperlihatkan cara menyesuaikan isi respons detail masalah.

Menyesuaikan detail masalah

Pembuatan ProblemDetails otomatis dapat dikustomisasi menggunakan salah satu opsi berikut:

  1. Menggunakan ProblemDetailsOptions.CustomizeProblemDetails
  2. Menggunakan kustom IProblemDetailsWriter
  3. IProblemDetailsService Memanggil di middleware

CustomizeProblemDetails operasi

Detail masalah yang dihasilkan dapat disesuaikan menggunakan CustomizeProblemDetails, dan kustomisasi diterapkan ke semua detail masalah yang dihasilkan secara otomatis.

Kode berikut menggunakan ProblemDetailsOptions untuk mengatur CustomizeProblemDetails:

builder.Services.AddProblemDetails(options =>
    options.CustomizeProblemDetails = ctx =>
            ctx.ProblemDetails.Extensions.Add("nodeId", Environment.MachineName));

var app = builder.Build();        

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
    app.UseHsts();
}

app.UseStatusCodePages();

Misalnya, HTTP Status 400 Bad Request hasil titik akhir menghasilkan isi respons detail masalah berikut:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "Bad Request",
  "status": 400,
  "nodeId": "my-machine-name"
}

Kustom IProblemDetailsWriter

Implementasi IProblemDetailsWriter dapat dibuat untuk kustomisasi tingkat lanjut.

public class SampleProblemDetailsWriter : IProblemDetailsWriter
{
    // Indicates that only responses with StatusCode == 400
    // are handled by this writer. All others are
    // handled by different registered writers if available.
    public bool CanWrite(ProblemDetailsContext context)
        => context.HttpContext.Response.StatusCode == 400;

    public ValueTask WriteAsync(ProblemDetailsContext context)
    {
        // Additional customizations.

        // Write to the response.
        var response = context.HttpContext.Response;
        return new ValueTask(response.WriteAsJsonAsync(context.ProblemDetails));
    }
}

Catatan: Saat menggunakan kustom IProblemDetailsWriter, kustom IProblemDetailsWriter harus didaftarkan sebelum memanggil AddRazorPages, , AddControllers, AddControllersWithViewsatau AddMvc:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransient<IProblemDetailsWriter, SampleProblemDetailsWriter>();

var app = builder.Build();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsWriter>() is
            { } problemDetailsService)
        {

            if (problemDetailsService.CanWrite(new ProblemDetailsContext() { HttpContext = context }))
            {
                (string Detail, string Type) details = mathErrorFeature.MathError switch
                {
                    MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                        "https://en.wikipedia.org/wiki/Division_by_zero"),
                    _ => ("Negative or complex numbers are not valid input.",
                        "https://en.wikipedia.org/wiki/Square_root")
                };

                await problemDetailsService.WriteAsync(new ProblemDetailsContext
                {
                    HttpContext = context,
                    ProblemDetails =
                    {
                        Title = "Bad Input",
                        Detail = details.Detail,
                        Type = details.Type
                    }
                });
            }
        }
    }
});

// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
    if (denominator == 0)
    {
        var errorType = new MathErrorFeature
        {
            MathError = MathErrorType.DivisionByZeroError
        };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(numerator / denominator);
});

// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
    if (radicand < 0)
    {
        var errorType = new MathErrorFeature
        {
            MathError = MathErrorType.NegativeRadicandError
        };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(Math.Sqrt(radicand));
});

app.Run();

Detail masalah dari Middleware

Pendekatan alternatif untuk digunakan ProblemDetailsOptions adalah mengatur CustomizeProblemDetailsProblemDetails middleware. Respons detail masalah dapat ditulis dengan memanggil IProblemDetailsService.WriteAsync:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStatusCodePages();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsService>() is
                                                           { } problemDetailsService)
        {
            (string Detail, string Type) details = mathErrorFeature.MathError switch
            {
                MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                "https://en.wikipedia.org/wiki/Division_by_zero"),
                _ => ("Negative or complex numbers are not valid input.", 
                "https://en.wikipedia.org/wiki/Square_root")
            };

            await problemDetailsService.WriteAsync(new ProblemDetailsContext
            {
                HttpContext = context,
                ProblemDetails =
                {
                    Title = "Bad Input",
                    Detail = details.Detail,
                    Type = details.Type
                }
            });
        }
    }
});

// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
    if (denominator == 0)
    {
        var errorType = new MathErrorFeature { MathError =
                                               MathErrorType.DivisionByZeroError };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(numerator / denominator);
});

// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
    if (radicand < 0)
    {
        var errorType = new MathErrorFeature { MathError =
                                               MathErrorType.NegativeRadicandError };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(Math.Sqrt(radicand));
});

app.MapControllers();

app.Run();

Dalam kode sebelumnya, titik /divide akhir API minimal dan /squareroot mengembalikan respons masalah kustom yang diharapkan pada input kesalahan.

Titik akhir pengontrol API mengembalikan respons masalah default pada input kesalahan, bukan respons masalah kustom. Respons masalah default dikembalikan karena pengontrol API telah menulis ke aliran respons, Detail masalah untuk kode status kesalahan, sebelum IProblemDetailsService.WriteAsync dipanggil dan respons tidak ditulis lagi.

Berikut ini ValuesController mengembalikan BadRequestResult, yang menulis ke aliran respons dan oleh karena itu mencegah respons masalah kustom dikembalikan.

[Route("api/[controller]/[action]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // /api/values/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.DivisionByZeroError
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values/squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.NegativeRadicandError
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }

        return Ok(Math.Sqrt(radicand));
    }

}

Berikut ini Values3Controller mengembalikan ControllerBase.Problem sehingga hasil masalah kustom yang diharapkan dikembalikan:

[Route("api/[controller]/[action]")]
[ApiController]
public class Values3Controller : ControllerBase
{
    // /api/values3/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.DivisionByZeroError
            };
            HttpContext.Features.Set(errorType);
            return Problem(
                title: "Bad Input",
                detail: "Divison by zero is not defined.",
                type: "https://en.wikipedia.org/wiki/Division_by_zero",
                statusCode: StatusCodes.Status400BadRequest
                );
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values3/squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.NegativeRadicandError
            };
            HttpContext.Features.Set(errorType);
            return Problem(
                title: "Bad Input",
                detail: "Negative or complex numbers are not valid input.",
                type: "https://en.wikipedia.org/wiki/Square_root",
                statusCode: StatusCodes.Status400BadRequest
                );
        }

        return Ok(Math.Sqrt(radicand));
    }

}

Menghasilkan payload ProblemDetails untuk pengecualian

Pertimbangkan aplikasi berikut:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.MapControllers();
app.Run();

Di lingkungan non-pengembangan, ketika pengecualian terjadi, berikut ini adalah respons ProblemDetails standar yang dikembalikan ke klien:

{
"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1",
"title":"An error occurred while processing your request.",
"status":500,"traceId":"00-b644<snip>-00"
}

Untuk sebagian besar aplikasi, kode sebelumnya adalah semua yang diperlukan untuk pengecualian. Namun, bagian berikut menunjukkan cara mendapatkan respons masalah yang lebih rinci.

Alternatif untuk halaman handler pengecualian kustom adalah menyediakan lambda ke UseExceptionHandler. Menggunakan lambda memungkinkan akses ke kesalahan dan menulis respons detail masalah dengan IProblemDetailsService.WriteAsync:

using Microsoft.AspNetCore.Diagnostics;
using static System.Net.Mime.MediaTypeNames;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            context.Response.ContentType = Text.Plain;

            var title = "Bad Input";
            var detail = "Invalid input";
            var type = "https://errors.example.com/badInput";

            if (context.RequestServices.GetService<IProblemDetailsService>() is
                { } problemDetailsService)
            {
                var exceptionHandlerFeature =
               context.Features.Get<IExceptionHandlerFeature>();

                var exceptionType = exceptionHandlerFeature?.Error;
                if (exceptionType != null &&
                   exceptionType.Message.Contains("infinity"))
                {
                    title = "Argument exception";
                    detail = "Invalid input";
                    type = "https://errors.example.com/argumentException";
                }

                await problemDetailsService.WriteAsync(new ProblemDetailsContext
                {
                    HttpContext = context,
                    ProblemDetails =
                {
                    Title = title,
                    Detail = detail,
                    Type = type
                }
                });
            }
        });
    });
}

app.MapControllers();
app.Run();

Peringatan

Jangan melayani informasi kesalahan sensitif kepada klien. Melayani kesalahan adalah risiko keamanan.

Pendekatan alternatif untuk menghasilkan detail masalah adalah menggunakan paket NuGet pihak ketiga Hellang.Middleware.ProblemDetails yang dapat digunakan untuk memetakan pengecualian dan kesalahan klien ke detail masalah.

Sumber Daya Tambahan:

Oleh Tom Dykstra

Artikel ini membahas pendekatan umum untuk menangani kesalahan di aplikasi web ASP.NET Core. Lihat juga Menangani kesalahan di API web ASP.NET Core dan Menangani kesalahan di aplikasi API Minimal.

Halaman pengecualian pengembang

Halaman Pengecualian Pengembang menampilkan informasi terperinci tentang pengecualian permintaan yang tidak tertangani. aplikasi ASP.NET Core mengaktifkan halaman pengecualian pengembang secara default saat keduanya:

Halaman pengecualian pengembang berjalan lebih awal di alur middleware, sehingga dapat menangkap pengecualian yang tidak tertangani yang dilemparkan dalam middleware yang mengikuti.

Informasi pengecualian terperinci tidak boleh ditampilkan secara publik saat aplikasi berjalan di lingkungan Produksi. Untuk informasi selengkapnya tentang mengonfigurasi lingkungan, lihat Menggunakan beberapa lingkungan di ASP.NET Core.

Halaman Pengecualian Pengembang dapat menyertakan informasi berikut tentang pengecualian dan permintaan:

  • Pelacakan tumpukan
  • Parameter string kueri, jika ada
  • Cookies, jika ada
  • Header

Halaman Pengecualian Pengembang tidak dijamin untuk memberikan informasi apa pun. Gunakan Pengelogan untuk informasi kesalahan lengkap.

Halaman handler pengecualian

Untuk mengonfigurasi halaman penanganan kesalahan kustom untuk lingkungan Produksi, panggil UseExceptionHandler. Middleware penanganan pengecualian ini:

  • Menangkap dan mencatat pengecualian yang tidak tertangani.
  • Menjalankan kembali permintaan dalam alur alternatif menggunakan jalur yang ditunjukkan. Permintaan tidak dijalankan kembali jika respons telah dimulai. Kode yang dihasilkan templat menjalankan kembali permintaan menggunakan /Error jalur .

Peringatan

Jika alur alternatif melemparkan pengecualian sendiri, Exception Handling Middleware akan memunculkan kembali pengecualian asli.

Karena middleware ini dapat menjalankan kembali alur permintaan:

  • Middleware perlu menangani reentrancy dengan permintaan yang sama. Ini biasanya berarti membersihkan status mereka setelah memanggil _next atau menyimpan cache pemrosesan mereka untuk HttpContext menghindari mengulanginya. Saat berhadapan dengan isi permintaan, ini berarti buffering atau penembolokan hasil seperti pembaca Formulir.
  • UseExceptionHandler(IApplicationBuilder, String) Untuk kelebihan beban yang digunakan dalam templat, hanya jalur permintaan yang dimodifikasi, dan data rute dibersihkan. Data permintaan seperti header, metode, dan item semuanya digunakan kembali apa adanya.
  • Layanan terlingkup tetap sama.

Dalam contoh berikut, UseExceptionHandler menambahkan middleware penanganan pengecualian di lingkungan non-Pengembangan:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

Razor Templat aplikasi Halaman menyediakan halaman Kesalahan (.cshtml) dan PageModel kelas (ErrorModel) di folder Halaman. Untuk aplikasi MVC, templat proyek menyertakan Error metode tindakan dan tampilan Kesalahan untuk Home pengontrol.

Middleware penanganan pengecualian menjalankan kembali permintaan menggunakan metode HTTP asli . Jika titik akhir handler kesalahan dibatasi untuk sekumpulan metode HTTP tertentu, titik akhir hanya berjalan untuk metode HTTP tersebut. Misalnya, tindakan pengontrol MVC yang menggunakan [HttpGet] atribut hanya berjalan untuk permintaan GET. Untuk memastikan bahwa semua permintaan mencapai halaman penanganan kesalahan kustom, jangan batasi ke sekumpulan metode HTTP tertentu.

Untuk menangani pengecualian secara berbeda berdasarkan metode HTTP asli:

  • Untuk Razor Pages, buat beberapa metode handler. Misalnya, gunakan OnGet untuk menangani pengecualian GET dan gunakan OnPost untuk menangani pengecualian POST.
  • Untuk MVC, terapkan atribut kata kerja HTTP ke beberapa tindakan. Misalnya, gunakan [HttpGet] untuk menangani pengecualian GET dan gunakan [HttpPost] untuk menangani pengecualian POST.

Untuk mengizinkan pengguna yang tidak diaturentikasi untuk melihat halaman penanganan kesalahan kustom, pastikan pengguna tersebut mendukung akses anonim.

Mengakses pengecualian

Gunakan IExceptionHandlerPathFeature untuk mengakses pengecualian dan jalur permintaan asli dalam penangan kesalahan. Contoh berikut menggunakan IExceptionHandlerPathFeature untuk mendapatkan informasi selengkapnya tentang pengecualian yang dilemparkan:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
    public string? RequestId { get; set; }

    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    public string? ExceptionMessage { get; set; }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        var exceptionHandlerPathFeature =
            HttpContext.Features.Get<IExceptionHandlerPathFeature>();

        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            ExceptionMessage = "The file was not found.";
        }

        if (exceptionHandlerPathFeature?.Path == "/")
        {
            ExceptionMessage ??= string.Empty;
            ExceptionMessage += " Page: Home.";
        }
    }
}

Peringatan

Jangan melayani informasi kesalahan sensitif kepada klien. Melayani kesalahan adalah risiko keamanan.

Penangan pengecualian lambda

Alternatif untuk halaman handler pengecualian kustom adalah menyediakan lambda ke UseExceptionHandler. Menggunakan lambda memungkinkan akses ke kesalahan sebelum mengembalikan respons.

Kode berikut menggunakan lambda untuk penanganan pengecualian:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            // using static System.Net.Mime.MediaTypeNames;
            context.Response.ContentType = Text.Plain;

            await context.Response.WriteAsync("An exception was thrown.");

            var exceptionHandlerPathFeature =
                context.Features.Get<IExceptionHandlerPathFeature>();

            if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
            {
                await context.Response.WriteAsync(" The file was not found.");
            }

            if (exceptionHandlerPathFeature?.Path == "/")
            {
                await context.Response.WriteAsync(" Page: Home.");
            }
        });
    });

    app.UseHsts();
}

Peringatan

Jangan melayani informasi kesalahan sensitif kepada klien. Melayani kesalahan adalah risiko keamanan.

IExceptionHandler

IExceptionHandler adalah antarmuka yang memberi pengembang panggilan balik untuk menangani pengecualian yang diketahui di lokasi pusat.

IExceptionHandler implementasi didaftarkan dengan memanggil IServiceCollection.AddExceptionHandler<T>. Masa pakai IExceptionHandler instans adalah singleton. Beberapa implementasi dapat ditambahkan, dan dipanggil dalam urutan terdaftar.

Jika handler pengecualian menangani permintaan, handler tersebut dapat kembali true berhenti memproses. Jika pengecualian tidak ditangani oleh handler pengecualian apa pun, kontrol akan kembali ke perilaku dan opsi default dari middleware. Metrik dan log yang berbeda dipancarkan untuk pengecualian yang ditangani versus pengecualian yang tidak tertangani.

Contoh berikut menunjukkan IExceptionHandler implementasi:

using Microsoft.AspNetCore.Diagnostics;

namespace ErrorHandlingSample
{
    public class CustomExceptionHandler : IExceptionHandler
    {
        private readonly ILogger<CustomExceptionHandler> logger;
        public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
        {
            this.logger = logger;
        }
        public ValueTask<bool> TryHandleAsync(
            HttpContext httpContext,
            Exception exception,
            CancellationToken cancellationToken)
        {
            var exceptionMessage = exception.Message;
            logger.LogError(
                "Error Message: {exceptionMessage}, Time of occurrence {time}",
                exceptionMessage, DateTime.UtcNow);
            // Return false to continue with the default behavior
            // - or - return true to signal that this exception is handled
            return ValueTask.FromResult(false);
        }
    }
}

Contoh berikut menunjukkan cara mendaftarkan IExceptionHandler implementasi untuk injeksi dependensi:

using ErrorHandlingSample;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
builder.Services.AddExceptionHandler<CustomExceptionHandler>();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

// Remaining Program.cs code omitted for brevity

Saat kode sebelumnya berjalan di lingkungan Pengembangan:

  • dipanggil CustomExceptionHandler terlebih dahulu untuk menangani pengecualian.
  • Setelah mencatat pengecualian, TryHandleException metode mengembalikan false, sehingga halaman pengecualian pengembang ditampilkan.

Di lingkungan lain:

  • dipanggil CustomExceptionHandler terlebih dahulu untuk menangani pengecualian.
  • Setelah mencatat pengecualian, TryHandleException metode mengembalikan false, sehingga /Error halaman ditampilkan.

GunakanStatusCodePages

Secara default, aplikasi ASP.NET Core tidak menyediakan halaman kode status untuk kode status kesalahan HTTP, seperti 404 - Tidak Ditemukan. Saat aplikasi menetapkan kode status kesalahan HTTP 400-599 yang tidak memiliki isi, aplikasi mengembalikan kode status dan isi respons kosong. Untuk mengaktifkan penangan teks-saja default untuk kode status kesalahan umum, panggil UseStatusCodePages di Program.cs:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages();

Panggil UseStatusCodePages sebelum meminta penanganan middleware. Misalnya, panggil UseStatusCodePages sebelum Middleware File Statis dan Middleware Titik Akhir.

Saat UseStatusCodePages tidak digunakan, menavigasi ke URL tanpa titik akhir mengembalikan pesan kesalahan yang bergantung pada browser yang menunjukkan titik akhir tidak dapat ditemukan. Ketika UseStatusCodePages dipanggil, browser mengembalikan respons berikut:

Status Code: 404; Not Found

UseStatusCodePages biasanya tidak digunakan dalam produksi karena mengembalikan pesan yang tidak berguna bagi pengguna.

Catatan

Halaman kode status middleware tidak menangkap pengecualian. Untuk menyediakan halaman penanganan kesalahan kustom, gunakan halaman handler pengecualian.

GunakanStatusCodePages dengan string format

Untuk mengkustomisasi tipe konten respons dan teks, gunakan kelebihan beban UseStatusCodePages yang mengambil tipe konten dan format string:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

// using static System.Net.Mime.MediaTypeNames;
app.UseStatusCodePages(Text.Plain, "Status Code Page: {0}");

Dalam kode sebelumnya, {0} adalah tempat penampung untuk kode kesalahan.

UseStatusCodePages dengan string format biasanya tidak digunakan dalam produksi karena menampilkan pesan yang tidak berguna bagi pengguna.

GunakanStatusCodePages dengan lambda

Untuk menentukan penanganan kesalahan kustom dan kode penulisan respons, gunakan kelebihan beban UseStatusCodePages yang mengambil ekspresi lambda:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages(async statusCodeContext =>
{
    // using static System.Net.Mime.MediaTypeNames;
    statusCodeContext.HttpContext.Response.ContentType = Text.Plain;

    await statusCodeContext.HttpContext.Response.WriteAsync(
        $"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});

UseStatusCodePages dengan lambda biasanya tidak digunakan dalam produksi karena mengembalikan pesan yang tidak berguna bagi pengguna.

GunakanStatusCodePagesWithRedirects

Metode UseStatusCodePagesWithRedirects ekstensi:

  • Mengirim kode status 302 - Ditemukan ke klien.
  • Mengalihkan klien ke titik akhir penanganan kesalahan yang disediakan dalam templat URL. Titik akhir penanganan kesalahan biasanya menampilkan informasi kesalahan dan mengembalikan HTTP 200.
var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");

Templat URL dapat menyertakan {0} tempat penampung untuk kode status, seperti yang ditunjukkan dalam kode sebelumnya. Jika templat URL dimulai dengan ~ (tilde), ~ digantikan oleh aplikasi PathBase. Saat menentukan titik akhir di aplikasi, buat tampilan atau Razor halaman MVC untuk titik akhir.

Metode ini umumnya digunakan saat aplikasi:

  • Harus mengalihkan klien ke titik akhir yang berbeda, biasanya dalam kasus di mana aplikasi yang berbeda memproses kesalahan. Untuk aplikasi web, bilah alamat browser klien mencerminkan titik akhir yang dialihkan.
  • Tidak boleh mempertahankan dan mengembalikan kode status asli dengan respons pengalihan awal.

GunakanStatusCodePagesWithReExecute

Metode UseStatusCodePagesWithReExecute ekstensi:

  • Menghasilkan isi respons dengan mengeksekusi ulang alur permintaan menggunakan jalur alternatif.
  • Tidak mengubah kode status sebelum atau sesudah mengeksekusi ulang alur.

Eksekusi alur baru dapat mengubah kode status respons, karena alur baru memiliki kontrol penuh atas kode status. Jika alur baru tidak mengubah kode status, kode status asli akan dikirim ke klien.

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");

Jika titik akhir dalam aplikasi ditentukan, buat tampilan atau Razor halaman MVC untuk titik akhir.

Metode ini umumnya digunakan ketika aplikasi harus:

  • Proses permintaan tanpa mengalihkan ke titik akhir yang berbeda. Untuk aplikasi web, bilah alamat browser klien mencerminkan titik akhir yang awalnya diminta.
  • Pertahankan dan kembalikan kode status asli dengan respons.

Templat URL harus dimulai dengan / dan dapat menyertakan tempat penampung {0} untuk kode status. Untuk meneruskan kode status sebagai parameter string kueri, teruskan argumen kedua ke dalam UseStatusCodePagesWithReExecute. Contohnya:

var app = builder.Build();  
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");

Titik akhir yang memproses kesalahan bisa mendapatkan URL asli yang menghasilkan kesalahan, seperti yang ditunjukkan dalam contoh berikut:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class StatusCodeModel : PageModel
{
    public int OriginalStatusCode { get; set; }

    public string? OriginalPathAndQuery { get; set; }

    public void OnGet(int statusCode)
    {
        OriginalStatusCode = statusCode;

        var statusCodeReExecuteFeature =
            HttpContext.Features.Get<IStatusCodeReExecuteFeature>();

        if (statusCodeReExecuteFeature is not null)
        {
            OriginalPathAndQuery = string.Join(
                statusCodeReExecuteFeature.OriginalPathBase,
                statusCodeReExecuteFeature.OriginalPath,
                statusCodeReExecuteFeature.OriginalQueryString);
        }
    }
}

Karena middleware ini dapat menjalankan kembali alur permintaan:

  • Middleware perlu menangani reentrancy dengan permintaan yang sama. Ini biasanya berarti membersihkan status mereka setelah memanggil _next atau menyimpan cache pemrosesan mereka untuk HttpContext menghindari mengulanginya. Saat berhadapan dengan isi permintaan, ini berarti buffering atau penembolokan hasil seperti pembaca Formulir.
  • Layanan terlingkup tetap sama.

Menonaktifkan halaman kode status

Untuk menonaktifkan halaman kode status untuk pengontrol MVC atau metode tindakan, gunakan atribut [SkipStatusCodePages ].

Untuk menonaktifkan halaman kode status untuk permintaan tertentu dalam Razor metode handler Pages atau di pengontrol MVC, gunakan IStatusCodePagesFeature:

public void OnGet()
{
    var statusCodePagesFeature =
        HttpContext.Features.Get<IStatusCodePagesFeature>();

    if (statusCodePagesFeature is not null)
    {
        statusCodePagesFeature.Enabled = false;
    }
}

Kode penanganan pengecualian

Kode dalam halaman penanganan pengecualian juga dapat melemparkan pengecualian. Halaman kesalahan produksi harus diuji secara menyeluruh dan berhati-hatilah untuk menghindari melempar pengecualian sendiri.

Header respons

Setelah header untuk respons dikirim:

  • Aplikasi tidak dapat mengubah kode status respons.
  • Halaman pengecualian atau handler apa pun tidak dapat dijalankan. Respons harus diselesaikan atau koneksi dibatalkan.

Penanganan pengecualian server

Selain logika penanganan pengecualian dalam aplikasi, implementasi server HTTP dapat menangani beberapa pengecualian. Jika server menangkap pengecualian sebelum header respons dikirim, server mengirim 500 - Internal Server Error respons tanpa isi respons. Jika server menangkap pengecualian setelah header respons dikirim, server menutup koneksi. Permintaan yang tidak ditangani oleh aplikasi ditangani oleh server. Pengecualian apa pun yang terjadi ketika server menangani permintaan ditangani oleh penanganan pengecualian server. Halaman kesalahan kustom aplikasi, middleware penanganan pengecualian, dan filter tidak memengaruhi perilaku ini.

Penanganan pengecualian startup

Hanya lapisan hosting yang dapat menangani pengecualian yang terjadi selama startup aplikasi. Host dapat dikonfigurasi untuk menangkap kesalahan startup dan menangkap kesalahan terperinci.

Lapisan hosting dapat menampilkan halaman kesalahan untuk kesalahan startup yang diambil hanya jika kesalahan terjadi setelah alamat host/pengikatan port. Jika pengikatan gagal:

  • Lapisan hosting mencatat pengecualian penting.
  • Proses dotnet mengalami crash.
  • Tidak ada halaman kesalahan yang ditampilkan ketika server HTTP adalah Kestrel.

Saat berjalan di IIS (atau Azure App Service) atau IIS Express, 502.5 - Kegagalan Proses dikembalikan oleh Modul Inti ASP.NET jika proses tidak dapat dimulai. Untuk informasi selengkapnya, lihat Memecahkan masalah ASP.NET Core di Azure App Service dan IIS.

Halaman kesalahan database

Filter AddDatabaseDeveloperPageExceptionFilter pengecualian halaman pengembang Database menangkap pengecualian terkait database yang dapat diselesaikan dengan menggunakan migrasi Entity Framework Core. Ketika pengecualian ini terjadi, respons HTML dihasilkan dengan detail kemungkinan tindakan untuk menyelesaikan masalah. Halaman ini hanya diaktifkan di lingkungan Pengembangan. Kode berikut menambahkan filter pengecualian halaman pengembang Database:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();

Filter pengecualian

Di aplikasi MVC, filter pengecualian dapat dikonfigurasi secara global atau berdasarkan per pengontrol atau per tindakan. Di Razor aplikasi Pages, aplikasi tersebut dapat dikonfigurasi secara global atau per model halaman. Filter ini menangani pengecualian yang tidak tertangani yang terjadi selama eksekusi tindakan pengontrol atau filter lain. Untuk informasi selengkapnya, lihat Filter di ASP.NET Core.

Filter pengecualian berguna untuk menjebak pengecualian yang terjadi dalam tindakan MVC, tetapi tidak fleksibel seperti middleware penanganan pengecualian bawaan, UseExceptionHandler. Sebaiknya gunakan UseExceptionHandler, kecuali Anda perlu melakukan penanganan kesalahan secara berbeda berdasarkan tindakan MVC mana yang dipilih.

Kesalahan status model

Untuk informasi tentang cara menangani kesalahan status model, lihat Pengikatan model dan Validasi model.

Detail masalah

Detail Masalah bukan satu-satunya format respons untuk menjelaskan kesalahan API HTTP, namun, biasanya digunakan untuk melaporkan kesalahan untuk API HTTP.

Layanan detail masalah mengimplementasikan IProblemDetailsService antarmuka, yang mendukung pembuatan detail masalah di ASP.NET Core. Metode AddProblemDetails ekstensi pada IServiceCollection mendaftarkan implementasi default IProblemDetailsService .

Di aplikasi ASP.NET Core, middleware berikut menghasilkan respons HTTP detail masalah saat AddProblemDetails dipanggil, kecuali ketika Accept header HTTP permintaan tidak menyertakan salah satu jenis konten yang didukung oleh yang terdaftar IProblemDetailsWriter (default: application/json):

Kode berikut mengonfigurasi aplikasi untuk menghasilkan respons detail masalah untuk semua respons kesalahan klien HTTP dan server yang belum memiliki konten isi:

builder.Services.AddProblemDetails();

var app = builder.Build();        

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
    app.UseHsts();
}

app.UseStatusCodePages();

Bagian berikutnya memperlihatkan cara menyesuaikan isi respons detail masalah.

Menyesuaikan detail masalah

Pembuatan ProblemDetails otomatis dapat dikustomisasi menggunakan salah satu opsi berikut:

  1. Menggunakan ProblemDetailsOptions.CustomizeProblemDetails
  2. Menggunakan kustom IProblemDetailsWriter
  3. IProblemDetailsService Memanggil di middleware

CustomizeProblemDetails operasi

Detail masalah yang dihasilkan dapat disesuaikan menggunakan CustomizeProblemDetails, dan kustomisasi diterapkan ke semua detail masalah yang dihasilkan secara otomatis.

Kode berikut menggunakan ProblemDetailsOptions untuk mengatur CustomizeProblemDetails:

builder.Services.AddProblemDetails(options =>
    options.CustomizeProblemDetails = ctx =>
            ctx.ProblemDetails.Extensions.Add("nodeId", Environment.MachineName));

var app = builder.Build();        

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
    app.UseHsts();
}

app.UseStatusCodePages();

Misalnya, HTTP Status 400 Bad Request hasil titik akhir menghasilkan isi respons detail masalah berikut:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "Bad Request",
  "status": 400,
  "nodeId": "my-machine-name"
}

Kustom IProblemDetailsWriter

Implementasi IProblemDetailsWriter dapat dibuat untuk kustomisasi tingkat lanjut.

public class SampleProblemDetailsWriter : IProblemDetailsWriter
{
    // Indicates that only responses with StatusCode == 400
    // are handled by this writer. All others are
    // handled by different registered writers if available.
    public bool CanWrite(ProblemDetailsContext context)
        => context.HttpContext.Response.StatusCode == 400;

    public ValueTask WriteAsync(ProblemDetailsContext context)
    {
        // Additional customizations.

        // Write to the response.
        var response = context.HttpContext.Response;
        return new ValueTask(response.WriteAsJsonAsync(context.ProblemDetails));
    }
}

Catatan: Saat menggunakan kustom IProblemDetailsWriter, kustom IProblemDetailsWriter harus didaftarkan sebelum memanggil AddRazorPages, , AddControllers, AddControllersWithViewsatau AddMvc:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransient<IProblemDetailsWriter, SampleProblemDetailsWriter>();

var app = builder.Build();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsWriter>() is
            { } problemDetailsService)
        {

            if (problemDetailsService.CanWrite(new ProblemDetailsContext() { HttpContext = context }))
            {
                (string Detail, string Type) details = mathErrorFeature.MathError switch
                {
                    MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                        "https://en.wikipedia.org/wiki/Division_by_zero"),
                    _ => ("Negative or complex numbers are not valid input.",
                        "https://en.wikipedia.org/wiki/Square_root")
                };

                await problemDetailsService.WriteAsync(new ProblemDetailsContext
                {
                    HttpContext = context,
                    ProblemDetails =
                    {
                        Title = "Bad Input",
                        Detail = details.Detail,
                        Type = details.Type
                    }
                });
            }
        }
    }
});

// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
    if (denominator == 0)
    {
        var errorType = new MathErrorFeature
        {
            MathError = MathErrorType.DivisionByZeroError
        };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(numerator / denominator);
});

// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
    if (radicand < 0)
    {
        var errorType = new MathErrorFeature
        {
            MathError = MathErrorType.NegativeRadicandError
        };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(Math.Sqrt(radicand));
});

app.Run();

Detail masalah dari Middleware

Pendekatan alternatif untuk digunakan ProblemDetailsOptions adalah mengatur CustomizeProblemDetailsProblemDetails middleware. Respons detail masalah dapat ditulis dengan memanggil IProblemDetailsService.WriteAsync:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStatusCodePages();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsService>() is
                                                           { } problemDetailsService)
        {
            (string Detail, string Type) details = mathErrorFeature.MathError switch
            {
                MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                "https://en.wikipedia.org/wiki/Division_by_zero"),
                _ => ("Negative or complex numbers are not valid input.", 
                "https://en.wikipedia.org/wiki/Square_root")
            };

            await problemDetailsService.WriteAsync(new ProblemDetailsContext
            {
                HttpContext = context,
                ProblemDetails =
                {
                    Title = "Bad Input",
                    Detail = details.Detail,
                    Type = details.Type
                }
            });
        }
    }
});

// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
    if (denominator == 0)
    {
        var errorType = new MathErrorFeature { MathError =
                                               MathErrorType.DivisionByZeroError };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(numerator / denominator);
});

// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
    if (radicand < 0)
    {
        var errorType = new MathErrorFeature { MathError =
                                               MathErrorType.NegativeRadicandError };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(Math.Sqrt(radicand));
});

app.MapControllers();

app.Run();

Dalam kode sebelumnya, titik /divide akhir API minimal dan /squareroot mengembalikan respons masalah kustom yang diharapkan pada input kesalahan.

Titik akhir pengontrol API mengembalikan respons masalah default pada input kesalahan, bukan respons masalah kustom. Respons masalah default dikembalikan karena pengontrol API telah menulis ke aliran respons, Detail masalah untuk kode status kesalahan, sebelum IProblemDetailsService.WriteAsync dipanggil dan respons tidak ditulis lagi.

Berikut ini ValuesController mengembalikan BadRequestResult, yang menulis ke aliran respons dan oleh karena itu mencegah respons masalah kustom dikembalikan.

[Route("api/[controller]/[action]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // /api/values/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.DivisionByZeroError
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values/squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.NegativeRadicandError
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }

        return Ok(Math.Sqrt(radicand));
    }

}

Berikut ini Values3Controller mengembalikan ControllerBase.Problem sehingga hasil masalah kustom yang diharapkan dikembalikan:

[Route("api/[controller]/[action]")]
[ApiController]
public class Values3Controller : ControllerBase
{
    // /api/values3/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.DivisionByZeroError
            };
            HttpContext.Features.Set(errorType);
            return Problem(
                title: "Bad Input",
                detail: "Divison by zero is not defined.",
                type: "https://en.wikipedia.org/wiki/Division_by_zero",
                statusCode: StatusCodes.Status400BadRequest
                );
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values3/squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.NegativeRadicandError
            };
            HttpContext.Features.Set(errorType);
            return Problem(
                title: "Bad Input",
                detail: "Negative or complex numbers are not valid input.",
                type: "https://en.wikipedia.org/wiki/Square_root",
                statusCode: StatusCodes.Status400BadRequest
                );
        }

        return Ok(Math.Sqrt(radicand));
    }

}

Menghasilkan payload ProblemDetails untuk pengecualian

Pertimbangkan aplikasi berikut:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.MapControllers();
app.Run();

Di lingkungan non-pengembangan, ketika pengecualian terjadi, berikut ini adalah respons ProblemDetails standar yang dikembalikan ke klien:

{
"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1",
"title":"An error occurred while processing your request.",
"status":500,"traceId":"00-b644<snip>-00"
}

Untuk sebagian besar aplikasi, kode sebelumnya adalah semua yang diperlukan untuk pengecualian. Namun, bagian berikut menunjukkan cara mendapatkan respons masalah yang lebih rinci.

Alternatif untuk halaman handler pengecualian kustom adalah menyediakan lambda ke UseExceptionHandler. Menggunakan lambda memungkinkan akses ke kesalahan dan menulis respons detail masalah dengan IProblemDetailsService.WriteAsync:

using Microsoft.AspNetCore.Diagnostics;
using static System.Net.Mime.MediaTypeNames;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            context.Response.ContentType = Text.Plain;

            var title = "Bad Input";
            var detail = "Invalid input";
            var type = "https://errors.example.com/badInput";

            if (context.RequestServices.GetService<IProblemDetailsService>() is
                { } problemDetailsService)
            {
                var exceptionHandlerFeature =
               context.Features.Get<IExceptionHandlerFeature>();

                var exceptionType = exceptionHandlerFeature?.Error;
                if (exceptionType != null &&
                   exceptionType.Message.Contains("infinity"))
                {
                    title = "Argument exception";
                    detail = "Invalid input";
                    type = "https://errors.example.com/argumentException";
                }

                await problemDetailsService.WriteAsync(new ProblemDetailsContext
                {
                    HttpContext = context,
                    ProblemDetails =
                {
                    Title = title,
                    Detail = detail,
                    Type = type
                }
                });
            }
        });
    });
}

app.MapControllers();
app.Run();

Peringatan

Jangan melayani informasi kesalahan sensitif kepada klien. Melayani kesalahan adalah risiko keamanan.

Pendekatan alternatif untuk menghasilkan detail masalah adalah menggunakan paket NuGet pihak ketiga Hellang.Middleware.ProblemDetails yang dapat digunakan untuk memetakan pengecualian dan kesalahan klien ke detail masalah.

Sumber Daya Tambahan:

Oleh Tom Dykstra

Artikel ini membahas pendekatan umum untuk menangani kesalahan di aplikasi web ASP.NET Core. Lihat juga Menangani kesalahan di API web ASP.NET Core dan Menangani kesalahan di aplikasi API Minimal.

Halaman pengecualian pengembang

Halaman Pengecualian Pengembang menampilkan informasi terperinci tentang pengecualian permintaan yang tidak tertangani. aplikasi ASP.NET Core mengaktifkan halaman pengecualian pengembang secara default saat keduanya:

Halaman pengecualian pengembang berjalan lebih awal di alur middleware, sehingga dapat menangkap pengecualian yang tidak tertangani yang dilemparkan dalam middleware yang mengikuti.

Informasi pengecualian terperinci tidak boleh ditampilkan secara publik saat aplikasi berjalan di lingkungan Produksi. Untuk informasi selengkapnya tentang mengonfigurasi lingkungan, lihat Menggunakan beberapa lingkungan di ASP.NET Core.

Halaman Pengecualian Pengembang dapat menyertakan informasi berikut tentang pengecualian dan permintaan:

  • Pelacakan tumpukan
  • Parameter string kueri, jika ada
  • Cookies, jika ada
  • Header

Halaman Pengecualian Pengembang tidak dijamin untuk memberikan informasi apa pun. Gunakan Pengelogan untuk informasi kesalahan lengkap.

Halaman handler pengecualian

Untuk mengonfigurasi halaman penanganan kesalahan kustom untuk lingkungan Produksi, panggil UseExceptionHandler. Middleware penanganan pengecualian ini:

  • Menangkap dan mencatat pengecualian yang tidak tertangani.
  • Menjalankan kembali permintaan dalam alur alternatif menggunakan jalur yang ditunjukkan. Permintaan tidak dijalankan kembali jika respons telah dimulai. Kode yang dihasilkan templat menjalankan kembali permintaan menggunakan /Error jalur .

Peringatan

Jika alur alternatif melemparkan pengecualian sendiri, Exception Handling Middleware akan memunculkan kembali pengecualian asli.

Karena middleware ini dapat menjalankan kembali alur permintaan:

  • Middleware perlu menangani reentrancy dengan permintaan yang sama. Ini biasanya berarti membersihkan status mereka setelah memanggil _next atau menyimpan cache pemrosesan mereka untuk HttpContext menghindari mengulanginya. Saat berhadapan dengan isi permintaan, ini berarti buffering atau penembolokan hasil seperti pembaca Formulir.
  • UseExceptionHandler(IApplicationBuilder, String) Untuk kelebihan beban yang digunakan dalam templat, hanya jalur permintaan yang dimodifikasi, dan data rute dibersihkan. Data permintaan seperti header, metode, dan item semuanya digunakan kembali apa adanya.
  • Layanan terlingkup tetap sama.

Dalam contoh berikut, UseExceptionHandler menambahkan middleware penanganan pengecualian di lingkungan non-Pengembangan:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

Razor Templat aplikasi Halaman menyediakan halaman Kesalahan (.cshtml) dan PageModel kelas (ErrorModel) di folder Halaman. Untuk aplikasi MVC, templat proyek menyertakan Error metode tindakan dan tampilan Kesalahan untuk Home pengontrol.

Middleware penanganan pengecualian menjalankan kembali permintaan menggunakan metode HTTP asli . Jika titik akhir handler kesalahan dibatasi untuk sekumpulan metode HTTP tertentu, titik akhir hanya berjalan untuk metode HTTP tersebut. Misalnya, tindakan pengontrol MVC yang menggunakan [HttpGet] atribut hanya berjalan untuk permintaan GET. Untuk memastikan bahwa semua permintaan mencapai halaman penanganan kesalahan kustom, jangan batasi ke sekumpulan metode HTTP tertentu.

Untuk menangani pengecualian secara berbeda berdasarkan metode HTTP asli:

  • Untuk Razor Pages, buat beberapa metode handler. Misalnya, gunakan OnGet untuk menangani pengecualian GET dan gunakan OnPost untuk menangani pengecualian POST.
  • Untuk MVC, terapkan atribut kata kerja HTTP ke beberapa tindakan. Misalnya, gunakan [HttpGet] untuk menangani pengecualian GET dan gunakan [HttpPost] untuk menangani pengecualian POST.

Untuk mengizinkan pengguna yang tidak diaturentikasi untuk melihat halaman penanganan kesalahan kustom, pastikan pengguna tersebut mendukung akses anonim.

Mengakses pengecualian

Gunakan IExceptionHandlerPathFeature untuk mengakses pengecualian dan jalur permintaan asli dalam penangan kesalahan. Contoh berikut menggunakan IExceptionHandlerPathFeature untuk mendapatkan informasi selengkapnya tentang pengecualian yang dilemparkan:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
    public string? RequestId { get; set; }

    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    public string? ExceptionMessage { get; set; }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        var exceptionHandlerPathFeature =
            HttpContext.Features.Get<IExceptionHandlerPathFeature>();

        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            ExceptionMessage = "The file was not found.";
        }

        if (exceptionHandlerPathFeature?.Path == "/")
        {
            ExceptionMessage ??= string.Empty;
            ExceptionMessage += " Page: Home.";
        }
    }
}

Peringatan

Jangan melayani informasi kesalahan sensitif kepada klien. Melayani kesalahan adalah risiko keamanan.

Penangan pengecualian lambda

Alternatif untuk halaman handler pengecualian kustom adalah menyediakan lambda ke UseExceptionHandler. Menggunakan lambda memungkinkan akses ke kesalahan sebelum mengembalikan respons.

Kode berikut menggunakan lambda untuk penanganan pengecualian:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            // using static System.Net.Mime.MediaTypeNames;
            context.Response.ContentType = Text.Plain;

            await context.Response.WriteAsync("An exception was thrown.");

            var exceptionHandlerPathFeature =
                context.Features.Get<IExceptionHandlerPathFeature>();

            if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
            {
                await context.Response.WriteAsync(" The file was not found.");
            }

            if (exceptionHandlerPathFeature?.Path == "/")
            {
                await context.Response.WriteAsync(" Page: Home.");
            }
        });
    });

    app.UseHsts();
}

Peringatan

Jangan melayani informasi kesalahan sensitif kepada klien. Melayani kesalahan adalah risiko keamanan.

GunakanStatusCodePages

Secara default, aplikasi ASP.NET Core tidak menyediakan halaman kode status untuk kode status kesalahan HTTP, seperti 404 - Tidak Ditemukan. Saat aplikasi menetapkan kode status kesalahan HTTP 400-599 yang tidak memiliki isi, aplikasi mengembalikan kode status dan isi respons kosong. Untuk mengaktifkan penangan teks-saja default untuk kode status kesalahan umum, panggil UseStatusCodePages di Program.cs:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages();

Panggil UseStatusCodePages sebelum meminta penanganan middleware. Misalnya, panggil UseStatusCodePages sebelum Middleware File Statis dan Middleware Titik Akhir.

Saat UseStatusCodePages tidak digunakan, menavigasi ke URL tanpa titik akhir mengembalikan pesan kesalahan yang bergantung pada browser yang menunjukkan titik akhir tidak dapat ditemukan. Ketika UseStatusCodePages dipanggil, browser mengembalikan respons berikut:

Status Code: 404; Not Found

UseStatusCodePages biasanya tidak digunakan dalam produksi karena mengembalikan pesan yang tidak berguna bagi pengguna.

Catatan

Halaman kode status middleware tidak menangkap pengecualian. Untuk menyediakan halaman penanganan kesalahan kustom, gunakan halaman handler pengecualian.

GunakanStatusCodePages dengan string format

Untuk mengkustomisasi tipe konten respons dan teks, gunakan kelebihan beban UseStatusCodePages yang mengambil tipe konten dan format string:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

// using static System.Net.Mime.MediaTypeNames;
app.UseStatusCodePages(Text.Plain, "Status Code Page: {0}");

Dalam kode sebelumnya, {0} adalah tempat penampung untuk kode kesalahan.

UseStatusCodePages dengan string format biasanya tidak digunakan dalam produksi karena menampilkan pesan yang tidak berguna bagi pengguna.

GunakanStatusCodePages dengan lambda

Untuk menentukan penanganan kesalahan kustom dan kode penulisan respons, gunakan kelebihan beban UseStatusCodePages yang mengambil ekspresi lambda:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages(async statusCodeContext =>
{
    // using static System.Net.Mime.MediaTypeNames;
    statusCodeContext.HttpContext.Response.ContentType = Text.Plain;

    await statusCodeContext.HttpContext.Response.WriteAsync(
        $"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});

UseStatusCodePages dengan lambda biasanya tidak digunakan dalam produksi karena mengembalikan pesan yang tidak berguna bagi pengguna.

GunakanStatusCodePagesWithRedirects

Metode UseStatusCodePagesWithRedirects ekstensi:

  • Mengirim kode status 302 - Ditemukan ke klien.
  • Mengalihkan klien ke titik akhir penanganan kesalahan yang disediakan dalam templat URL. Titik akhir penanganan kesalahan biasanya menampilkan informasi kesalahan dan mengembalikan HTTP 200.
var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");

Templat URL dapat menyertakan {0} tempat penampung untuk kode status, seperti yang ditunjukkan dalam kode sebelumnya. Jika templat URL dimulai dengan ~ (tilde), ~ digantikan oleh aplikasi PathBase. Saat menentukan titik akhir di aplikasi, buat tampilan atau Razor halaman MVC untuk titik akhir.

Metode ini umumnya digunakan saat aplikasi:

  • Harus mengalihkan klien ke titik akhir yang berbeda, biasanya dalam kasus di mana aplikasi yang berbeda memproses kesalahan. Untuk aplikasi web, bilah alamat browser klien mencerminkan titik akhir yang dialihkan.
  • Tidak boleh mempertahankan dan mengembalikan kode status asli dengan respons pengalihan awal.

GunakanStatusCodePagesWithReExecute

Metode UseStatusCodePagesWithReExecute ekstensi:

  • Menghasilkan isi respons dengan mengeksekusi ulang alur permintaan menggunakan jalur alternatif.
  • Tidak mengubah kode status sebelum atau sesudah mengeksekusi ulang alur.

Eksekusi alur baru dapat mengubah kode status respons, karena alur baru memiliki kontrol penuh atas kode status. Jika alur baru tidak mengubah kode status, kode status asli akan dikirim ke klien.

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");

Jika titik akhir dalam aplikasi ditentukan, buat tampilan atau Razor halaman MVC untuk titik akhir.

Metode ini umumnya digunakan ketika aplikasi harus:

  • Proses permintaan tanpa mengalihkan ke titik akhir yang berbeda. Untuk aplikasi web, bilah alamat browser klien mencerminkan titik akhir yang awalnya diminta.
  • Pertahankan dan kembalikan kode status asli dengan respons.

Templat URL harus dimulai dengan / dan dapat menyertakan tempat penampung {0} untuk kode status. Untuk meneruskan kode status sebagai parameter string kueri, teruskan argumen kedua ke dalam UseStatusCodePagesWithReExecute. Contohnya:

var app = builder.Build();  
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");

Titik akhir yang memproses kesalahan bisa mendapatkan URL asli yang menghasilkan kesalahan, seperti yang ditunjukkan dalam contoh berikut:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class StatusCodeModel : PageModel
{
    public int OriginalStatusCode { get; set; }

    public string? OriginalPathAndQuery { get; set; }

    public void OnGet(int statusCode)
    {
        OriginalStatusCode = statusCode;

        var statusCodeReExecuteFeature =
            HttpContext.Features.Get<IStatusCodeReExecuteFeature>();

        if (statusCodeReExecuteFeature is not null)
        {
            OriginalPathAndQuery = string.Join(
                statusCodeReExecuteFeature.OriginalPathBase,
                statusCodeReExecuteFeature.OriginalPath,
                statusCodeReExecuteFeature.OriginalQueryString);
        }
    }
}

Karena middleware ini dapat menjalankan kembali alur permintaan:

  • Middleware perlu menangani reentrancy dengan permintaan yang sama. Ini biasanya berarti membersihkan status mereka setelah memanggil _next atau menyimpan cache pemrosesan mereka untuk HttpContext menghindari mengulanginya. Saat berhadapan dengan isi permintaan, ini berarti buffering atau penembolokan hasil seperti pembaca Formulir.
  • Layanan terlingkup tetap sama.

Menonaktifkan halaman kode status

Untuk menonaktifkan halaman kode status untuk pengontrol MVC atau metode tindakan, gunakan atribut [SkipStatusCodePages ].

Untuk menonaktifkan halaman kode status untuk permintaan tertentu dalam Razor metode handler Pages atau di pengontrol MVC, gunakan IStatusCodePagesFeature:

public void OnGet()
{
    var statusCodePagesFeature =
        HttpContext.Features.Get<IStatusCodePagesFeature>();

    if (statusCodePagesFeature is not null)
    {
        statusCodePagesFeature.Enabled = false;
    }
}

Kode penanganan pengecualian

Kode dalam halaman penanganan pengecualian juga dapat melemparkan pengecualian. Halaman kesalahan produksi harus diuji secara menyeluruh dan berhati-hatilah untuk menghindari melempar pengecualian sendiri.

Header respons

Setelah header untuk respons dikirim:

  • Aplikasi tidak dapat mengubah kode status respons.
  • Halaman pengecualian atau handler apa pun tidak dapat dijalankan. Respons harus diselesaikan atau koneksi dibatalkan.

Penanganan pengecualian server

Selain logika penanganan pengecualian dalam aplikasi, implementasi server HTTP dapat menangani beberapa pengecualian. Jika server menangkap pengecualian sebelum header respons dikirim, server mengirim 500 - Internal Server Error respons tanpa isi respons. Jika server menangkap pengecualian setelah header respons dikirim, server menutup koneksi. Permintaan yang tidak ditangani oleh aplikasi ditangani oleh server. Pengecualian apa pun yang terjadi ketika server menangani permintaan ditangani oleh penanganan pengecualian server. Halaman kesalahan kustom aplikasi, middleware penanganan pengecualian, dan filter tidak memengaruhi perilaku ini.

Penanganan pengecualian startup

Hanya lapisan hosting yang dapat menangani pengecualian yang terjadi selama startup aplikasi. Host dapat dikonfigurasi untuk menangkap kesalahan startup dan menangkap kesalahan terperinci.

Lapisan hosting dapat menampilkan halaman kesalahan untuk kesalahan startup yang diambil hanya jika kesalahan terjadi setelah alamat host/pengikatan port. Jika pengikatan gagal:

  • Lapisan hosting mencatat pengecualian penting.
  • Proses dotnet mengalami crash.
  • Tidak ada halaman kesalahan yang ditampilkan ketika server HTTP adalah Kestrel.

Saat berjalan di IIS (atau Azure App Service) atau IIS Express, 502.5 - Kegagalan Proses dikembalikan oleh Modul Inti ASP.NET jika proses tidak dapat dimulai. Untuk informasi selengkapnya, lihat Memecahkan masalah ASP.NET Core di Azure App Service dan IIS.

Halaman kesalahan database

Filter AddDatabaseDeveloperPageExceptionFilter pengecualian halaman pengembang Database menangkap pengecualian terkait database yang dapat diselesaikan dengan menggunakan migrasi Entity Framework Core. Ketika pengecualian ini terjadi, respons HTML dihasilkan dengan detail kemungkinan tindakan untuk menyelesaikan masalah. Halaman ini hanya diaktifkan di lingkungan Pengembangan. Kode berikut menambahkan filter pengecualian halaman pengembang Database:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();

Filter pengecualian

Di aplikasi MVC, filter pengecualian dapat dikonfigurasi secara global atau berdasarkan per pengontrol atau per tindakan. Di Razor aplikasi Pages, aplikasi tersebut dapat dikonfigurasi secara global atau per model halaman. Filter ini menangani pengecualian yang tidak tertangani yang terjadi selama eksekusi tindakan pengontrol atau filter lain. Untuk informasi selengkapnya, lihat Filter di ASP.NET Core.

Filter pengecualian berguna untuk menjebak pengecualian yang terjadi dalam tindakan MVC, tetapi tidak fleksibel seperti middleware penanganan pengecualian bawaan, UseExceptionHandler. Sebaiknya gunakan UseExceptionHandler, kecuali Anda perlu melakukan penanganan kesalahan secara berbeda berdasarkan tindakan MVC mana yang dipilih.

Kesalahan status model

Untuk informasi tentang cara menangani kesalahan status model, lihat Pengikatan model dan Validasi model.

Detail masalah

Detail Masalah bukan satu-satunya format respons untuk menjelaskan kesalahan API HTTP, namun, biasanya digunakan untuk melaporkan kesalahan untuk API HTTP.

Layanan detail masalah mengimplementasikan IProblemDetailsService antarmuka, yang mendukung pembuatan detail masalah di ASP.NET Core. Metode AddProblemDetails ekstensi pada IServiceCollection mendaftarkan implementasi default IProblemDetailsService .

Di aplikasi ASP.NET Core, middleware berikut menghasilkan respons HTTP detail masalah saat AddProblemDetails dipanggil, kecuali ketika Accept header HTTP permintaan tidak menyertakan salah satu jenis konten yang didukung oleh yang terdaftar IProblemDetailsWriter (default: application/json):

Kode berikut mengonfigurasi aplikasi untuk menghasilkan respons detail masalah untuk semua respons kesalahan klien HTTP dan server yang belum memiliki konten isi:

builder.Services.AddProblemDetails();

var app = builder.Build();        

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
    app.UseHsts();
}

app.UseStatusCodePages();

Bagian berikutnya memperlihatkan cara menyesuaikan isi respons detail masalah.

Menyesuaikan detail masalah

Pembuatan ProblemDetails otomatis dapat dikustomisasi menggunakan salah satu opsi berikut:

  1. Menggunakan ProblemDetailsOptions.CustomizeProblemDetails
  2. Menggunakan kustom IProblemDetailsWriter
  3. IProblemDetailsService Memanggil di middleware

CustomizeProblemDetails operasi

Detail masalah yang dihasilkan dapat disesuaikan menggunakan CustomizeProblemDetails, dan kustomisasi diterapkan ke semua detail masalah yang dihasilkan secara otomatis.

Kode berikut menggunakan ProblemDetailsOptions untuk mengatur CustomizeProblemDetails:

builder.Services.AddProblemDetails(options =>
    options.CustomizeProblemDetails = ctx =>
            ctx.ProblemDetails.Extensions.Add("nodeId", Environment.MachineName));

var app = builder.Build();        

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
    app.UseHsts();
}

app.UseStatusCodePages();

Misalnya, HTTP Status 400 Bad Request hasil titik akhir menghasilkan isi respons detail masalah berikut:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "Bad Request",
  "status": 400,
  "nodeId": "my-machine-name"
}

Kustom IProblemDetailsWriter

Implementasi IProblemDetailsWriter dapat dibuat untuk kustomisasi tingkat lanjut.

public class SampleProblemDetailsWriter : IProblemDetailsWriter
{
    // Indicates that only responses with StatusCode == 400
    // are handled by this writer. All others are
    // handled by different registered writers if available.
    public bool CanWrite(ProblemDetailsContext context)
        => context.HttpContext.Response.StatusCode == 400;

    public ValueTask WriteAsync(ProblemDetailsContext context)
    {
        // Additional customizations.

        // Write to the response.
        var response = context.HttpContext.Response;
        return new ValueTask(response.WriteAsJsonAsync(context.ProblemDetails));
    }
}

Catatan: Saat menggunakan kustom IProblemDetailsWriter, kustom IProblemDetailsWriter harus didaftarkan sebelum memanggil AddRazorPages, , AddControllers, AddControllersWithViewsatau AddMvc:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransient<IProblemDetailsWriter, SampleProblemDetailsWriter>();

var app = builder.Build();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsWriter>() is
            { } problemDetailsService)
        {

            if (problemDetailsService.CanWrite(new ProblemDetailsContext() { HttpContext = context }))
            {
                (string Detail, string Type) details = mathErrorFeature.MathError switch
                {
                    MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                        "https://en.wikipedia.org/wiki/Division_by_zero"),
                    _ => ("Negative or complex numbers are not valid input.",
                        "https://en.wikipedia.org/wiki/Square_root")
                };

                await problemDetailsService.WriteAsync(new ProblemDetailsContext
                {
                    HttpContext = context,
                    ProblemDetails =
                    {
                        Title = "Bad Input",
                        Detail = details.Detail,
                        Type = details.Type
                    }
                });
            }
        }
    }
});

// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
    if (denominator == 0)
    {
        var errorType = new MathErrorFeature
        {
            MathError = MathErrorType.DivisionByZeroError
        };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(numerator / denominator);
});

// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
    if (radicand < 0)
    {
        var errorType = new MathErrorFeature
        {
            MathError = MathErrorType.NegativeRadicandError
        };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(Math.Sqrt(radicand));
});

app.Run();

Detail masalah dari Middleware

Pendekatan alternatif untuk digunakan ProblemDetailsOptions adalah mengatur CustomizeProblemDetailsProblemDetails middleware. Respons detail masalah dapat ditulis dengan memanggil IProblemDetailsService.WriteAsync:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStatusCodePages();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsService>() is
                                                           { } problemDetailsService)
        {
            (string Detail, string Type) details = mathErrorFeature.MathError switch
            {
                MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                "https://en.wikipedia.org/wiki/Division_by_zero"),
                _ => ("Negative or complex numbers are not valid input.", 
                "https://en.wikipedia.org/wiki/Square_root")
            };

            await problemDetailsService.WriteAsync(new ProblemDetailsContext
            {
                HttpContext = context,
                ProblemDetails =
                {
                    Title = "Bad Input",
                    Detail = details.Detail,
                    Type = details.Type
                }
            });
        }
    }
});

// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
    if (denominator == 0)
    {
        var errorType = new MathErrorFeature { MathError =
                                               MathErrorType.DivisionByZeroError };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(numerator / denominator);
});

// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
    if (radicand < 0)
    {
        var errorType = new MathErrorFeature { MathError =
                                               MathErrorType.NegativeRadicandError };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(Math.Sqrt(radicand));
});

app.MapControllers();

app.Run();

Dalam kode sebelumnya, titik /divide akhir API minimal dan /squareroot mengembalikan respons masalah kustom yang diharapkan pada input kesalahan.

Titik akhir pengontrol API mengembalikan respons masalah default pada input kesalahan, bukan respons masalah kustom. Respons masalah default dikembalikan karena pengontrol API telah menulis ke aliran respons, Detail masalah untuk kode status kesalahan, sebelum IProblemDetailsService.WriteAsync dipanggil dan respons tidak ditulis lagi.

Berikut ini ValuesController mengembalikan BadRequestResult, yang menulis ke aliran respons dan oleh karena itu mencegah respons masalah kustom dikembalikan.

[Route("api/[controller]/[action]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // /api/values/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.DivisionByZeroError
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values/squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.NegativeRadicandError
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }

        return Ok(Math.Sqrt(radicand));
    }

}

Berikut ini Values3Controller mengembalikan ControllerBase.Problem sehingga hasil masalah kustom yang diharapkan dikembalikan:

[Route("api/[controller]/[action]")]
[ApiController]
public class Values3Controller : ControllerBase
{
    // /api/values3/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.DivisionByZeroError
            };
            HttpContext.Features.Set(errorType);
            return Problem(
                title: "Bad Input",
                detail: "Divison by zero is not defined.",
                type: "https://en.wikipedia.org/wiki/Division_by_zero",
                statusCode: StatusCodes.Status400BadRequest
                );
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values3/squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.NegativeRadicandError
            };
            HttpContext.Features.Set(errorType);
            return Problem(
                title: "Bad Input",
                detail: "Negative or complex numbers are not valid input.",
                type: "https://en.wikipedia.org/wiki/Square_root",
                statusCode: StatusCodes.Status400BadRequest
                );
        }

        return Ok(Math.Sqrt(radicand));
    }

}

Menghasilkan payload ProblemDetails untuk pengecualian

Pertimbangkan aplikasi berikut:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.MapControllers();
app.Run();

Di lingkungan non-pengembangan, ketika pengecualian terjadi, berikut ini adalah respons ProblemDetails standar yang dikembalikan ke klien:

{
"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1",
"title":"An error occurred while processing your request.",
"status":500,"traceId":"00-b644<snip>-00"
}

Untuk sebagian besar aplikasi, kode sebelumnya adalah semua yang diperlukan untuk pengecualian. Namun, bagian berikut menunjukkan cara mendapatkan respons masalah yang lebih rinci.

Alternatif untuk halaman handler pengecualian kustom adalah menyediakan lambda ke UseExceptionHandler. Menggunakan lambda memungkinkan akses ke kesalahan dan menulis respons detail masalah dengan IProblemDetailsService.WriteAsync:

using Microsoft.AspNetCore.Diagnostics;
using static System.Net.Mime.MediaTypeNames;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            context.Response.ContentType = Text.Plain;

            var title = "Bad Input";
            var detail = "Invalid input";
            var type = "https://errors.example.com/badInput";

            if (context.RequestServices.GetService<IProblemDetailsService>() is
                { } problemDetailsService)
            {
                var exceptionHandlerFeature =
               context.Features.Get<IExceptionHandlerFeature>();

                var exceptionType = exceptionHandlerFeature?.Error;
                if (exceptionType != null &&
                   exceptionType.Message.Contains("infinity"))
                {
                    title = "Argument exception";
                    detail = "Invalid input";
                    type = "https://errors.example.com/argumentException";
                }

                await problemDetailsService.WriteAsync(new ProblemDetailsContext
                {
                    HttpContext = context,
                    ProblemDetails =
                {
                    Title = title,
                    Detail = detail,
                    Type = type
                }
                });
            }
        });
    });
}

app.MapControllers();
app.Run();

Peringatan

Jangan melayani informasi kesalahan sensitif kepada klien. Melayani kesalahan adalah risiko keamanan.

Pendekatan alternatif untuk menghasilkan detail masalah adalah menggunakan paket NuGet pihak ketiga Hellang.Middleware.ProblemDetails yang dapat digunakan untuk memetakan pengecualian dan kesalahan klien ke detail masalah.

Sumber Daya Tambahan:

Oleh Tom Dykstra

Artikel ini membahas pendekatan umum untuk menangani kesalahan di aplikasi web ASP.NET Core. Lihat Menangani kesalahan di API web ASP.NET Core untuk API web.

Halaman pengecualian pengembang

Halaman Pengecualian Pengembang menampilkan informasi terperinci tentang pengecualian permintaan yang tidak tertangani. aplikasi ASP.NET Core mengaktifkan halaman pengecualian pengembang secara default saat keduanya:

Halaman pengecualian pengembang berjalan lebih awal di alur middleware, sehingga dapat menangkap pengecualian yang tidak tertangani yang dilemparkan dalam middleware yang mengikuti.

Informasi pengecualian terperinci tidak boleh ditampilkan secara publik saat aplikasi berjalan di lingkungan Produksi. Untuk informasi selengkapnya tentang mengonfigurasi lingkungan, lihat Menggunakan beberapa lingkungan di ASP.NET Core.

Halaman Pengecualian Pengembang dapat menyertakan informasi berikut tentang pengecualian dan permintaan:

  • Pelacakan tumpukan
  • Parameter string kueri, jika ada
  • Cookies, jika ada
  • Header

Halaman Pengecualian Pengembang tidak dijamin untuk memberikan informasi apa pun. Gunakan Pengelogan untuk informasi kesalahan lengkap.

Halaman handler pengecualian

Untuk mengonfigurasi halaman penanganan kesalahan kustom untuk lingkungan Produksi, panggil UseExceptionHandler. Middleware penanganan pengecualian ini:

  • Menangkap dan mencatat pengecualian yang tidak tertangani.
  • Menjalankan kembali permintaan dalam alur alternatif menggunakan jalur yang ditunjukkan. Permintaan tidak dijalankan kembali jika respons telah dimulai. Kode yang dihasilkan templat menjalankan kembali permintaan menggunakan /Error jalur .

Peringatan

Jika alur alternatif melemparkan pengecualian sendiri, Exception Handling Middleware akan memunculkan kembali pengecualian asli.

Dalam contoh berikut, UseExceptionHandler menambahkan middleware penanganan pengecualian di lingkungan non-Pengembangan:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

Razor Templat aplikasi Halaman menyediakan halaman Kesalahan (.cshtml) dan PageModel kelas (ErrorModel) di folder Halaman. Untuk aplikasi MVC, templat proyek menyertakan Error metode tindakan dan tampilan Kesalahan untuk Home pengontrol.

Middleware penanganan pengecualian menjalankan kembali permintaan menggunakan metode HTTP asli . Jika titik akhir handler kesalahan dibatasi untuk sekumpulan metode HTTP tertentu, titik akhir hanya berjalan untuk metode HTTP tersebut. Misalnya, tindakan pengontrol MVC yang menggunakan [HttpGet] atribut hanya berjalan untuk permintaan GET. Untuk memastikan bahwa semua permintaan mencapai halaman penanganan kesalahan kustom, jangan batasi ke sekumpulan metode HTTP tertentu.

Untuk menangani pengecualian secara berbeda berdasarkan metode HTTP asli:

  • Untuk Razor Pages, buat beberapa metode handler. Misalnya, gunakan OnGet untuk menangani pengecualian GET dan gunakan OnPost untuk menangani pengecualian POST.
  • Untuk MVC, terapkan atribut kata kerja HTTP ke beberapa tindakan. Misalnya, gunakan [HttpGet] untuk menangani pengecualian GET dan gunakan [HttpPost] untuk menangani pengecualian POST.

Untuk mengizinkan pengguna yang tidak diaturentikasi untuk melihat halaman penanganan kesalahan kustom, pastikan pengguna tersebut mendukung akses anonim.

Mengakses pengecualian

Gunakan IExceptionHandlerPathFeature untuk mengakses pengecualian dan jalur permintaan asli dalam penangan kesalahan. Contoh berikut menggunakan IExceptionHandlerPathFeature untuk mendapatkan informasi selengkapnya tentang pengecualian yang dilemparkan:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
    public string? RequestId { get; set; }

    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    public string? ExceptionMessage { get; set; }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        var exceptionHandlerPathFeature =
            HttpContext.Features.Get<IExceptionHandlerPathFeature>();

        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            ExceptionMessage = "The file was not found.";
        }

        if (exceptionHandlerPathFeature?.Path == "/")
        {
            ExceptionMessage ??= string.Empty;
            ExceptionMessage += " Page: Home.";
        }
    }
}

Peringatan

Jangan melayani informasi kesalahan sensitif kepada klien. Melayani kesalahan adalah risiko keamanan.

Penangan pengecualian lambda

Alternatif untuk halaman handler pengecualian kustom adalah menyediakan lambda ke UseExceptionHandler. Menggunakan lambda memungkinkan akses ke kesalahan sebelum mengembalikan respons.

Kode berikut menggunakan lambda untuk penanganan pengecualian:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            // using static System.Net.Mime.MediaTypeNames;
            context.Response.ContentType = Text.Plain;

            await context.Response.WriteAsync("An exception was thrown.");

            var exceptionHandlerPathFeature =
                context.Features.Get<IExceptionHandlerPathFeature>();

            if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
            {
                await context.Response.WriteAsync(" The file was not found.");
            }

            if (exceptionHandlerPathFeature?.Path == "/")
            {
                await context.Response.WriteAsync(" Page: Home.");
            }
        });
    });

    app.UseHsts();
}

Peringatan

Jangan melayani informasi kesalahan sensitif kepada klien. Melayani kesalahan adalah risiko keamanan.

GunakanStatusCodePages

Secara default, aplikasi ASP.NET Core tidak menyediakan halaman kode status untuk kode status kesalahan HTTP, seperti 404 - Tidak Ditemukan. Saat aplikasi menetapkan kode status kesalahan HTTP 400-599 yang tidak memiliki isi, aplikasi mengembalikan kode status dan isi respons kosong. Untuk mengaktifkan penangan teks-saja default untuk kode status kesalahan umum, panggil UseStatusCodePages di Program.cs:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages();

Panggil UseStatusCodePages sebelum meminta penanganan middleware. Misalnya, panggil UseStatusCodePages sebelum Middleware File Statis dan Middleware Titik Akhir.

Saat UseStatusCodePages tidak digunakan, menavigasi ke URL tanpa titik akhir mengembalikan pesan kesalahan yang bergantung pada browser yang menunjukkan titik akhir tidak dapat ditemukan. Ketika UseStatusCodePages dipanggil, browser mengembalikan respons berikut:

Status Code: 404; Not Found

UseStatusCodePages biasanya tidak digunakan dalam produksi karena mengembalikan pesan yang tidak berguna bagi pengguna.

Catatan

Halaman kode status middleware tidak menangkap pengecualian. Untuk menyediakan halaman penanganan kesalahan kustom, gunakan halaman handler pengecualian.

GunakanStatusCodePages dengan string format

Untuk mengkustomisasi tipe konten respons dan teks, gunakan kelebihan beban UseStatusCodePages yang mengambil tipe konten dan format string:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

// using static System.Net.Mime.MediaTypeNames;
app.UseStatusCodePages(Text.Plain, "Status Code Page: {0}");

Dalam kode sebelumnya, {0} adalah tempat penampung untuk kode kesalahan.

UseStatusCodePages dengan string format biasanya tidak digunakan dalam produksi karena menampilkan pesan yang tidak berguna bagi pengguna.

GunakanStatusCodePages dengan lambda

Untuk menentukan penanganan kesalahan kustom dan kode penulisan respons, gunakan kelebihan beban UseStatusCodePages yang mengambil ekspresi lambda:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages(async statusCodeContext =>
{
    // using static System.Net.Mime.MediaTypeNames;
    statusCodeContext.HttpContext.Response.ContentType = Text.Plain;

    await statusCodeContext.HttpContext.Response.WriteAsync(
        $"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});

UseStatusCodePages dengan lambda biasanya tidak digunakan dalam produksi karena mengembalikan pesan yang tidak berguna bagi pengguna.

GunakanStatusCodePagesWithRedirects

Metode UseStatusCodePagesWithRedirects ekstensi:

  • Mengirim kode status 302 - Ditemukan ke klien.
  • Mengalihkan klien ke titik akhir penanganan kesalahan yang disediakan dalam templat URL. Titik akhir penanganan kesalahan biasanya menampilkan informasi kesalahan dan mengembalikan HTTP 200.
var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");

Templat URL dapat menyertakan {0} tempat penampung untuk kode status, seperti yang ditunjukkan dalam kode sebelumnya. Jika templat URL dimulai dengan ~ (tilde), ~ digantikan oleh aplikasi PathBase. Saat menentukan titik akhir di aplikasi, buat tampilan atau Razor halaman MVC untuk titik akhir.

Metode ini umumnya digunakan saat aplikasi:

  • Harus mengalihkan klien ke titik akhir yang berbeda, biasanya dalam kasus di mana aplikasi yang berbeda memproses kesalahan. Untuk aplikasi web, bilah alamat browser klien mencerminkan titik akhir yang dialihkan.
  • Tidak boleh mempertahankan dan mengembalikan kode status asli dengan respons pengalihan awal.

GunakanStatusCodePagesWithReExecute

Metode UseStatusCodePagesWithReExecute ekstensi:

  • Mengembalikan kode status asli ke klien.
  • Menghasilkan isi respons dengan mengeksekusi ulang alur permintaan menggunakan jalur alternatif.
var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");

Jika titik akhir dalam aplikasi ditentukan, buat tampilan atau Razor halaman MVC untuk titik akhir.

Metode ini umumnya digunakan ketika aplikasi harus:

  • Proses permintaan tanpa mengalihkan ke titik akhir yang berbeda. Untuk aplikasi web, bilah alamat browser klien mencerminkan titik akhir yang awalnya diminta.
  • Pertahankan dan kembalikan kode status asli dengan respons.

Templat URL harus dimulai dengan / dan dapat menyertakan tempat penampung {0} untuk kode status. Untuk meneruskan kode status sebagai parameter string kueri, teruskan argumen kedua ke dalam UseStatusCodePagesWithReExecute. Contohnya:

app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");

Titik akhir yang memproses kesalahan bisa mendapatkan URL asli yang menghasilkan kesalahan, seperti yang ditunjukkan dalam contoh berikut:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class StatusCodeModel : PageModel
{
    public int OriginalStatusCode { get; set; }

    public string? OriginalPathAndQuery { get; set; }

    public void OnGet(int statusCode)
    {
        OriginalStatusCode = statusCode;

        var statusCodeReExecuteFeature =
            HttpContext.Features.Get<IStatusCodeReExecuteFeature>();

        if (statusCodeReExecuteFeature is not null)
        {
            OriginalPathAndQuery = string.Join(
                statusCodeReExecuteFeature.OriginalPathBase,
                statusCodeReExecuteFeature.OriginalPath,
                statusCodeReExecuteFeature.OriginalQueryString);
        }
    }
}

Menonaktifkan halaman kode status

Untuk menonaktifkan halaman kode status untuk pengontrol MVC atau metode tindakan, gunakan atribut [SkipStatusCodePages ].

Untuk menonaktifkan halaman kode status untuk permintaan tertentu dalam Razor metode handler Pages atau di pengontrol MVC, gunakan IStatusCodePagesFeature:

public void OnGet()
{
    var statusCodePagesFeature =
        HttpContext.Features.Get<IStatusCodePagesFeature>();

    if (statusCodePagesFeature is not null)
    {
        statusCodePagesFeature.Enabled = false;
    }
}

Kode penanganan pengecualian

Kode dalam halaman penanganan pengecualian juga dapat melemparkan pengecualian. Halaman kesalahan produksi harus diuji secara menyeluruh dan berhati-hatilah untuk menghindari melempar pengecualian sendiri.

Header respons

Setelah header untuk respons dikirim:

  • Aplikasi tidak dapat mengubah kode status respons.
  • Halaman pengecualian atau handler apa pun tidak dapat dijalankan. Respons harus diselesaikan atau koneksi dibatalkan.

Penanganan pengecualian server

Selain logika penanganan pengecualian dalam aplikasi, implementasi server HTTP dapat menangani beberapa pengecualian. Jika server menangkap pengecualian sebelum header respons dikirim, server mengirim 500 - Internal Server Error respons tanpa isi respons. Jika server menangkap pengecualian setelah header respons dikirim, server menutup koneksi. Permintaan yang tidak ditangani oleh aplikasi ditangani oleh server. Pengecualian apa pun yang terjadi ketika server menangani permintaan ditangani oleh penanganan pengecualian server. Halaman kesalahan kustom aplikasi, middleware penanganan pengecualian, dan filter tidak memengaruhi perilaku ini.

Penanganan pengecualian startup

Hanya lapisan hosting yang dapat menangani pengecualian yang terjadi selama startup aplikasi. Host dapat dikonfigurasi untuk menangkap kesalahan startup dan menangkap kesalahan terperinci.

Lapisan hosting dapat menampilkan halaman kesalahan untuk kesalahan startup yang diambil hanya jika kesalahan terjadi setelah alamat host/pengikatan port. Jika pengikatan gagal:

  • Lapisan hosting mencatat pengecualian penting.
  • Proses dotnet mengalami crash.
  • Tidak ada halaman kesalahan yang ditampilkan ketika server HTTP adalah Kestrel.

Saat berjalan di IIS (atau Azure App Service) atau IIS Express, 502.5 - Kegagalan Proses dikembalikan oleh Modul Inti ASP.NET jika proses tidak dapat dimulai. Untuk informasi selengkapnya, lihat Memecahkan masalah ASP.NET Core di Azure App Service dan IIS.

Halaman kesalahan database

Filter AddDatabaseDeveloperPageExceptionFilter pengecualian halaman pengembang Database menangkap pengecualian terkait database yang dapat diselesaikan dengan menggunakan migrasi Entity Framework Core. Ketika pengecualian ini terjadi, respons HTML dihasilkan dengan detail kemungkinan tindakan untuk menyelesaikan masalah. Halaman ini hanya diaktifkan di lingkungan Pengembangan. Kode berikut menambahkan filter pengecualian halaman pengembang Database:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();

Filter pengecualian

Di aplikasi MVC, filter pengecualian dapat dikonfigurasi secara global atau berdasarkan per pengontrol atau per tindakan. Di Razor aplikasi Pages, aplikasi tersebut dapat dikonfigurasi secara global atau per model halaman. Filter ini menangani pengecualian yang tidak tertangani yang terjadi selama eksekusi tindakan pengontrol atau filter lain. Untuk informasi selengkapnya, lihat Filter di ASP.NET Core.

Filter pengecualian berguna untuk menjebak pengecualian yang terjadi dalam tindakan MVC, tetapi tidak fleksibel seperti middleware penanganan pengecualian bawaan, UseExceptionHandler. Sebaiknya gunakan UseExceptionHandler, kecuali Anda perlu melakukan penanganan kesalahan secara berbeda berdasarkan tindakan MVC mana yang dipilih.

Kesalahan status model

Untuk informasi tentang cara menangani kesalahan status model, lihat Pengikatan model dan Validasi model.

Sumber Daya Tambahan:

Oleh Kirk Larkin, Tom Dykstra, dan Steve Smith

Artikel ini membahas pendekatan umum untuk menangani kesalahan di aplikasi web ASP.NET Core. Lihat Menangani kesalahan di API web ASP.NET Core untuk API web.

Melihat atau mengunduh kode sampel. (Cara mengunduh.) Tab jaringan pada alat pengembang browser F12 berguna saat menguji aplikasi sampel.

Halaman Pengecualian Pengembang

Halaman Pengecualian Pengembang menampilkan informasi terperinci tentang pengecualian permintaan yang tidak tertangani. Templat ASP.NET Core menghasilkan kode berikut:

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

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

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Kode yang disorot sebelumnya memungkinkan halaman pengecualian pengembang saat aplikasi berjalan di lingkungan Pengembangan.

Templat ditempatkan UseDeveloperExceptionPage di awal alur middleware sehingga dapat menangkap pengecualian yang tidak tertangani yang dilemparkan dalam middleware yang mengikuti.

Kode sebelumnya memungkinkan Halaman Pengecualian Pengembang hanya saat aplikasi berjalan di lingkungan Pengembangan. Informasi pengecualian terperinci tidak boleh ditampilkan secara publik saat aplikasi berjalan di lingkungan Produksi. Untuk informasi selengkapnya tentang mengonfigurasi lingkungan, lihat Menggunakan beberapa lingkungan di ASP.NET Core.

Halaman Pengecualian Pengembang dapat menyertakan informasi berikut tentang pengecualian dan permintaan:

  • Pelacakan tumpukan
  • Parameter string kueri jika ada
  • Cookies jika ada
  • Header

Halaman Pengecualian Pengembang tidak dijamin untuk memberikan informasi apa pun. Gunakan Pengelogan untuk informasi kesalahan lengkap.

Halaman handler pengecualian

Untuk mengonfigurasi halaman penanganan kesalahan kustom untuk lingkungan Produksi, panggil UseExceptionHandler. Middleware penanganan pengecualian ini:

  • Menangkap dan mencatat pengecualian yang tidak tertangani.
  • Menjalankan kembali permintaan dalam alur alternatif menggunakan jalur yang ditunjukkan. Permintaan tidak dijalankan kembali jika respons telah dimulai. Kode yang dihasilkan templat menjalankan kembali permintaan menggunakan /Error jalur .

Peringatan

Jika alur alternatif melemparkan pengecualian sendiri, Exception Handling Middleware akan memunculkan kembali pengecualian asli.

Dalam contoh berikut, UseExceptionHandler menambahkan middleware penanganan pengecualian di lingkungan non-Pengembangan:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

Razor Templat aplikasi Halaman menyediakan halaman Kesalahan (.cshtml) dan PageModel kelas (ErrorModel) di folder Halaman. Untuk aplikasi MVC, templat proyek menyertakan Error metode tindakan dan tampilan Kesalahan untuk Home pengontrol.

Middleware penanganan pengecualian menjalankan kembali permintaan menggunakan metode HTTP asli . Jika titik akhir handler kesalahan dibatasi untuk sekumpulan metode HTTP tertentu, titik akhir hanya berjalan untuk metode HTTP tersebut. Misalnya, tindakan pengontrol MVC yang menggunakan [HttpGet] atribut hanya berjalan untuk permintaan GET. Untuk memastikan bahwa semua permintaan mencapai halaman penanganan kesalahan kustom, jangan batasi ke sekumpulan metode HTTP tertentu.

Untuk menangani pengecualian secara berbeda berdasarkan metode HTTP asli:

  • Untuk Razor Pages, buat beberapa metode handler. Misalnya, gunakan OnGet untuk menangani pengecualian GET dan gunakan OnPost untuk menangani pengecualian POST.
  • Untuk MVC, terapkan atribut kata kerja HTTP ke beberapa tindakan. Misalnya, gunakan [HttpGet] untuk menangani pengecualian GET dan gunakan [HttpPost] untuk menangani pengecualian POST.

Untuk mengizinkan pengguna yang tidak diaturentikasi untuk melihat halaman penanganan kesalahan kustom, pastikan pengguna tersebut mendukung akses anonim.

Mengakses pengecualian

Gunakan IExceptionHandlerPathFeature untuk mengakses pengecualian dan jalur permintaan asli dalam penangan kesalahan. Kode berikut menambahkan ExceptionMessage ke default Pages/Error.cshtml.cs yang dihasilkan oleh templat ASP.NET Core:

[ResponseCache(Duration=0, Location=ResponseCacheLocation.None, NoStore=true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
    public string RequestId { get; set; }
    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
    public string ExceptionMessage { get; set; }
    private readonly ILogger<ErrorModel> _logger;

    public ErrorModel(ILogger<ErrorModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        var exceptionHandlerPathFeature =
        HttpContext.Features.Get<IExceptionHandlerPathFeature>();
        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            ExceptionMessage = "File error thrown";
            _logger.LogError(ExceptionMessage);
        }
        if (exceptionHandlerPathFeature?.Path == "/index")
        {
            ExceptionMessage += " from home page";
        }
    }
}

Peringatan

Jangan melayani informasi kesalahan sensitif kepada klien. Melayani kesalahan adalah risiko keamanan.

Untuk menguji pengecualian di aplikasi sampel:

  • Atur lingkungan ke produksi.
  • Hapus komentar dari webBuilder.UseStartup<Startup>(); dalam Program.cs.
  • Pilih Picu pengecualian di halaman beranda.

Penangan pengecualian lambda

Alternatif untuk halaman handler pengecualian kustom adalah menyediakan lambda ke UseExceptionHandler. Menggunakan lambda memungkinkan akses ke kesalahan sebelum mengembalikan respons.

Kode berikut menggunakan lambda untuk penanganan pengecualian:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler(errorApp =>
        {
            errorApp.Run(async context =>
            {
                context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;;
                context.Response.ContentType = "text/html";

                await context.Response.WriteAsync("<html lang=\"en\"><body>\r\n");
                await context.Response.WriteAsync("ERROR!<br><br>\r\n");

                var exceptionHandlerPathFeature =
                    context.Features.Get<IExceptionHandlerPathFeature>();

                if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
                {
                    await context.Response.WriteAsync(
                                              "File error thrown!<br><br>\r\n");
                }

                await context.Response.WriteAsync(
                                              "<a href=\"/\">Home</a><br>\r\n");
                await context.Response.WriteAsync("</body></html>\r\n");
                await context.Response.WriteAsync(new string(' ', 512)); 
            });
        });
        app.UseHsts();
    }

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

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Peringatan

Jangan melayani informasi kesalahan sensitif dari IExceptionHandlerFeature atau IExceptionHandlerPathFeature ke klien. Melayani kesalahan adalah risiko keamanan.

Untuk menguji penanganan pengecualian lambda di aplikasi sampel:

  • Atur lingkungan ke produksi.
  • Hapus komentar dari webBuilder.UseStartup<StartupLambda>(); dalam Program.cs.
  • Pilih Picu pengecualian di halaman beranda.

GunakanStatusCodePages

Secara default, aplikasi ASP.NET Core tidak menyediakan halaman kode status untuk kode status kesalahan HTTP, seperti 404 - Tidak Ditemukan. Saat aplikasi menetapkan kode status kesalahan HTTP 400-599 yang tidak memiliki isi, aplikasi mengembalikan kode status dan isi respons kosong. Untuk menyediakan halaman kode status, gunakan middleware halaman kode status. Untuk mengaktifkan penangan teks-saja default untuk kode status kesalahan umum, panggil UseStatusCodePages dalam Startup.Configure metode :

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

    app.UseStatusCodePages();

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

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Panggil UseStatusCodePages sebelum meminta penanganan middleware. Misalnya, panggil UseStatusCodePages sebelum Middleware File Statis dan Middleware Titik Akhir.

Saat UseStatusCodePages tidak digunakan, menavigasi ke URL tanpa titik akhir mengembalikan pesan kesalahan yang bergantung pada browser yang menunjukkan titik akhir tidak dapat ditemukan. Misalnya, menavigasi ke Home/Privacy2. Ketika UseStatusCodePages dipanggil, browser mengembalikan:

Status Code: 404; Not Found

UseStatusCodePages biasanya tidak digunakan dalam produksi karena mengembalikan pesan yang tidak berguna bagi pengguna.

Untuk menguji UseStatusCodePages di aplikasi sampel:

  • Atur lingkungan ke produksi.
  • Hapus komentar dari webBuilder.UseStartup<StartupUseStatusCodePages>(); dalam Program.cs.
  • Pilih tautan di halaman beranda di beranda.

Catatan

Halaman kode status middleware tidak menangkap pengecualian. Untuk menyediakan halaman penanganan kesalahan kustom, gunakan halaman handler pengecualian.

GunakanStatusCodePages dengan string format

Untuk mengkustomisasi tipe konten respons dan teks, gunakan kelebihan beban UseStatusCodePages yang mengambil tipe konten dan format string:

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

    app.UseStatusCodePages(
        "text/plain", "Status code page, status code: {0}");

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

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Dalam kode sebelumnya, {0} adalah tempat penampung untuk kode kesalahan.

UseStatusCodePages dengan string format biasanya tidak digunakan dalam produksi karena menampilkan pesan yang tidak berguna bagi pengguna.

Untuk menguji UseStatusCodePages di aplikasi sampel, hapus komentar dari webBuilder.UseStartup<StartupFormat>(); dalam Program.cs.

GunakanStatusCodePages dengan lambda

Untuk menentukan penanganan kesalahan kustom dan kode penulisan respons, gunakan kelebihan beban UseStatusCodePages yang mengambil ekspresi lambda:

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

    app.UseStatusCodePages(async context =>
    {
        context.HttpContext.Response.ContentType = "text/plain";

        await context.HttpContext.Response.WriteAsync(
            "Status code page, status code: " +
            context.HttpContext.Response.StatusCode);
    });

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

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

UseStatusCodePages dengan lambda biasanya tidak digunakan dalam produksi karena mengembalikan pesan yang tidak berguna bagi pengguna.

Untuk menguji UseStatusCodePages di aplikasi sampel, hapus komentar dari webBuilder.UseStartup<StartupStatusLambda>(); dalam Program.cs.

GunakanStatusCodePagesWithRedirects

Metode UseStatusCodePagesWithRedirects ekstensi:

  • Mengirim kode status 302 - Ditemukan ke klien.
  • Mengalihkan klien ke titik akhir penanganan kesalahan yang disediakan dalam templat URL. Titik akhir penanganan kesalahan biasanya menampilkan informasi kesalahan dan mengembalikan HTTP 200.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePagesWithRedirects("/MyStatusCode?code={0}");

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

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Templat URL dapat menyertakan {0} tempat penampung untuk kode status, seperti yang ditunjukkan dalam kode sebelumnya. Jika templat URL dimulai dengan ~ (tilde), ~ digantikan oleh aplikasi PathBase. Saat menentukan titik akhir di aplikasi, buat tampilan atau Razor halaman MVC untuk titik akhir. Razor Untuk contoh Halaman, lihat Pages/MyStatusCode.cshtml di aplikasi sampel.

Metode ini umumnya digunakan saat aplikasi:

  • Harus mengalihkan klien ke titik akhir yang berbeda, biasanya dalam kasus di mana aplikasi yang berbeda memproses kesalahan. Untuk aplikasi web, bilah alamat browser klien mencerminkan titik akhir yang dialihkan.
  • Tidak boleh mempertahankan dan mengembalikan kode status asli dengan respons pengalihan awal.

Untuk menguji UseStatusCodePages di aplikasi sampel, hapus komentar dari webBuilder.UseStartup<StartupSCredirect>(); dalam Program.cs.

GunakanStatusCodePagesWithReExecute

Metode UseStatusCodePagesWithReExecute ekstensi:

  • Mengembalikan kode status asli ke klien.
  • Menghasilkan isi respons dengan mengeksekusi ulang alur permintaan menggunakan jalur alternatif.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePagesWithReExecute("/MyStatusCode2", "?code={0}");

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

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Jika titik akhir dalam aplikasi ditentukan, buat tampilan atau Razor halaman MVC untuk titik akhir. Pastikan UseStatusCodePagesWithReExecute ditempatkan sebelumnya UseRouting sehingga permintaan dapat dialihkan ke halaman status. Razor Untuk contoh Halaman, lihat Pages/MyStatusCode2.cshtml di aplikasi sampel.

Metode ini umumnya digunakan ketika aplikasi harus:

  • Proses permintaan tanpa mengalihkan ke titik akhir yang berbeda. Untuk aplikasi web, bilah alamat browser klien mencerminkan titik akhir yang awalnya diminta.
  • Pertahankan dan kembalikan kode status asli dengan respons.

URL dan templat string kueri dapat menyertakan tempat penampung {0} untuk kode status. Templat URL harus dimulai dengan /.

Titik akhir yang memproses kesalahan bisa mendapatkan URL asli yang menghasilkan kesalahan, seperti yang ditunjukkan dalam contoh berikut:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class MyStatusCode2Model : PageModel
{
    public string RequestId { get; set; }
    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    public string ErrorStatusCode { get; set; }

    public string OriginalURL { get; set; }
    public bool ShowOriginalURL => !string.IsNullOrEmpty(OriginalURL);

    public void OnGet(string code)
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
        ErrorStatusCode = code;

        var statusCodeReExecuteFeature = HttpContext.Features.Get<
                                               IStatusCodeReExecuteFeature>();
        if (statusCodeReExecuteFeature != null)
        {
            OriginalURL =
                statusCodeReExecuteFeature.OriginalPathBase
                + statusCodeReExecuteFeature.OriginalPath
                + statusCodeReExecuteFeature.OriginalQueryString;
        }
    }
}

Razor Untuk contoh Halaman, lihat Pages/MyStatusCode2.cshtml di aplikasi sampel.

Untuk menguji UseStatusCodePages di aplikasi sampel, hapus komentar dari webBuilder.UseStartup<StartupSCreX>(); dalam Program.cs.

Menonaktifkan halaman kode status

Untuk menonaktifkan halaman kode status untuk pengontrol MVC atau metode tindakan, gunakan atribut [SkipStatusCodePages ].

Untuk menonaktifkan halaman kode status untuk permintaan tertentu dalam Razor metode handler Pages atau di pengontrol MVC, gunakan IStatusCodePagesFeature:

public void OnGet()
{
    // using Microsoft.AspNetCore.Diagnostics;
    var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();

    if (statusCodePagesFeature != null)
    {
        statusCodePagesFeature.Enabled = false;
    }
}

Kode penanganan pengecualian

Kode dalam halaman penanganan pengecualian juga dapat melemparkan pengecualian. Halaman kesalahan produksi harus diuji secara menyeluruh dan berhati-hatilah untuk menghindari melempar pengecualian sendiri.

Header respons

Setelah header untuk respons dikirim:

  • Aplikasi tidak dapat mengubah kode status respons.
  • Halaman pengecualian atau handler apa pun tidak dapat dijalankan. Respons harus diselesaikan atau koneksi dibatalkan.

Penanganan pengecualian server

Selain logika penanganan pengecualian dalam aplikasi, implementasi server HTTP dapat menangani beberapa pengecualian. Jika server menangkap pengecualian sebelum header respons dikirim, server mengirim 500 - Internal Server Error respons tanpa isi respons. Jika server menangkap pengecualian setelah header respons dikirim, server menutup koneksi. Permintaan yang tidak ditangani oleh aplikasi ditangani oleh server. Pengecualian apa pun yang terjadi ketika server menangani permintaan ditangani oleh penanganan pengecualian server. Halaman kesalahan kustom aplikasi, middleware penanganan pengecualian, dan filter tidak memengaruhi perilaku ini.

Penanganan pengecualian startup

Hanya lapisan hosting yang dapat menangani pengecualian yang terjadi selama startup aplikasi. Host dapat dikonfigurasi untuk menangkap kesalahan startup dan menangkap kesalahan terperinci.

Lapisan hosting dapat menampilkan halaman kesalahan untuk kesalahan startup yang diambil hanya jika kesalahan terjadi setelah alamat host/pengikatan port. Jika pengikatan gagal:

  • Lapisan hosting mencatat pengecualian penting.
  • Proses dotnet mengalami crash.
  • Tidak ada halaman kesalahan yang ditampilkan ketika server HTTP adalah Kestrel.

Saat berjalan di IIS (atau Azure App Service) atau IIS Express, 502.5 - Kegagalan Proses dikembalikan oleh Modul Inti ASP.NET jika proses tidak dapat dimulai. Untuk informasi selengkapnya, lihat Memecahkan masalah ASP.NET Core di Azure App Service dan IIS.

Halaman kesalahan database

Filter AddDatabaseDeveloperPageExceptionFilter pengecualian halaman pengembang Database menangkap pengecualian terkait database yang dapat diselesaikan dengan menggunakan migrasi Entity Framework Core. Ketika pengecualian ini terjadi, respons HTML dihasilkan dengan detail kemungkinan tindakan untuk menyelesaikan masalah. Halaman ini hanya diaktifkan di lingkungan Pengembangan. Kode berikut dihasilkan oleh templat ASP.NET Core Razor Pages saat akun pengguna individual ditentukan:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDatabaseDeveloperPageExceptionFilter();
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();
}

Filter pengecualian

Di aplikasi MVC, filter pengecualian dapat dikonfigurasi secara global atau berdasarkan per pengontrol atau per tindakan. Di Razor aplikasi Pages, aplikasi tersebut dapat dikonfigurasi secara global atau per model halaman. Filter ini menangani pengecualian yang tidak tertangani yang terjadi selama eksekusi tindakan pengontrol atau filter lain. Untuk informasi selengkapnya, lihat Filter di ASP.NET Core.

Filter pengecualian berguna untuk menjebak pengecualian yang terjadi dalam tindakan MVC, tetapi tidak fleksibel seperti middleware penanganan pengecualian bawaan, UseExceptionHandler. Sebaiknya gunakan UseExceptionHandler, kecuali Anda perlu melakukan penanganan kesalahan secara berbeda berdasarkan tindakan MVC mana yang dipilih.

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

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

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Kesalahan status model

Untuk informasi tentang cara menangani kesalahan status model, lihat Pengikatan model dan Validasi model.

Sumber Daya Tambahan:

Oleh Tom Dykstra, dan Steve Smith

Artikel ini membahas pendekatan umum untuk menangani kesalahan di aplikasi web ASP.NET Core. Lihat Menangani kesalahan di API web ASP.NET Core untuk API web.

Melihat atau mengunduh kode sampel. (Cara mengunduh.)

Halaman Pengecualian Pengembang

Halaman Pengecualian Pengembang menampilkan informasi terperinci tentang pengecualian permintaan. Templat ASP.NET Core menghasilkan kode berikut:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

Kode sebelumnya memungkinkan halaman pengecualian pengembang saat aplikasi berjalan di lingkungan Pengembangan.

Tempat templat UseDeveloperExceptionPage sebelum middleware apa pun sehingga pengecualian tertangkap dalam middleware yang mengikuti.

Kode sebelumnya memungkinkan Halaman Pengecualian Pengembang hanya saat aplikasi berjalan di lingkungan Pengembangan. Informasi pengecualian terperinci tidak boleh ditampilkan secara publik saat aplikasi berjalan dalam produksi. Untuk informasi selengkapnya tentang mengonfigurasi lingkungan, lihat Menggunakan beberapa lingkungan di ASP.NET Core.

Halaman Pengecualian Pengembang menyertakan informasi berikut tentang pengecualian dan permintaan:

  • Pelacakan tumpukan
  • Parameter string kueri jika ada
  • Cookies jika ada
  • Header

Halaman handler pengecualian

Untuk mengonfigurasi halaman penanganan kesalahan kustom untuk lingkungan Produksi, gunakan Middleware Penanganan Pengecualian. Middleware:

  • Menangkap dan mencatat pengecualian.
  • Menjalankan kembali permintaan dalam alur alternatif untuk halaman atau pengontrol yang ditunjukkan. Permintaan tidak dijalankan kembali jika respons telah dimulai. Kode yang dihasilkan templat menjalankan kembali permintaan ke /Error.

Dalam contoh berikut, UseExceptionHandler menambahkan Middleware Penanganan Pengecualian di lingkungan non-Pengembangan:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

Razor Templat aplikasi Halaman menyediakan halaman Kesalahan (.cshtml) dan PageModel kelas (ErrorModel) di folder Halaman. Untuk aplikasi MVC, templat proyek menyertakan metode tindakan Kesalahan dan tampilan Kesalahan di Home pengontrol.

Jangan tandai metode tindakan penangan kesalahan dengan atribut metode HTTP, seperti HttpGet. Kata kerja eksplisit mencegah beberapa permintaan mencapai metode . Izinkan akses anonim ke metode jika pengguna yang tidak diaturentikasi akan melihat tampilan kesalahan.

Mengakses pengecualian

Gunakan IExceptionHandlerPathFeature untuk mengakses pengecualian dan jalur permintaan asli di pengontrol atau halaman penangan kesalahan:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
    public string RequestId { get; set; }
    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
    public string ExceptionMessage { get; set; }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        var exceptionHandlerPathFeature =
            HttpContext.Features.Get<IExceptionHandlerPathFeature>();
        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            ExceptionMessage = "File error thrown";
        }
        if (exceptionHandlerPathFeature?.Path == "/index")
        {
            ExceptionMessage += " from home page";
        }
    }
}

Peringatan

Jangan melayani informasi kesalahan sensitif kepada klien. Melayani kesalahan adalah risiko keamanan.

Untuk memicu halaman penanganan pengecualian sebelumnya, atur lingkungan ke produksi dan paksa pengecualian.

Penangan pengecualian lambda

Alternatif untuk halaman handler pengecualian kustom adalah menyediakan lambda ke UseExceptionHandler. Menggunakan lambda memungkinkan akses ke kesalahan sebelum mengembalikan respons.

Berikut adalah contoh penggunaan lambda untuk penanganan pengecualian:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
   app.UseExceptionHandler(errorApp =>
   {
        errorApp.Run(async context =>
        {
            context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
            context.Response.ContentType = "text/html";

            await context.Response.WriteAsync("<html lang=\"en\"><body>\r\n");
            await context.Response.WriteAsync("ERROR!<br><br>\r\n");

            var exceptionHandlerPathFeature = 
                context.Features.Get<IExceptionHandlerPathFeature>();

            if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
            {
                await context.Response.WriteAsync("File error thrown!<br><br>\r\n");
            }

            await context.Response.WriteAsync("<a href=\"/\">Home</a><br>\r\n");
            await context.Response.WriteAsync("</body></html>\r\n");
            await context.Response.WriteAsync(new string(' ', 512)); // IE padding
        });
    });
    app.UseHsts();
}

Dalam kode sebelumnya, await context.Response.WriteAsync(new string(' ', 512)); ditambahkan sehingga browser Internet Explorer menampilkan pesan kesalahan daripada pesan kesalahan IE. Untuk informasi lebih lanjut, lihat masalah GitHub ini.

Peringatan

Jangan melayani informasi kesalahan sensitif dari IExceptionHandlerFeature atau IExceptionHandlerPathFeature ke klien. Melayani kesalahan adalah risiko keamanan.

Untuk melihat hasil penanganan pengecualian lambda di aplikasi sampel, gunakan ProdEnvironment arahan prapemrossor dan ErrorHandlerLambda , dan pilih Picu pengecualian di halaman beranda.

GunakanStatusCodePages

Secara default, aplikasi ASP.NET Core tidak menyediakan halaman kode status untuk kode status HTTP, seperti 404 - Tidak Ditemukan. Aplikasi mengembalikan kode status dan isi respons kosong. Untuk menyediakan halaman kode status, gunakan middleware Halaman Kode Status.

Middleware tersedia oleh paket Microsoft.AspNetCore.Diagnostics .

Untuk mengaktifkan penangan teks-saja default untuk kode status kesalahan umum, panggil UseStatusCodePages dalam Startup.Configure metode :

app.UseStatusCodePages();

Panggil UseStatusCodePages sebelum meminta penanganan middleware (misalnya, Middleware File Statis dan Middleware MVC).

Saat UseStatusCodePages tidak digunakan, menavigasi ke URL tanpa titik akhir mengembalikan pesan kesalahan dependen browser yang menunjukkan titik akhir tidak dapat ditemukan. Misalnya, menavigasi ke Home/Privacy2. Ketika UseStatusCodePages dipanggil, browser mengembalikan:

Status Code: 404; Not Found

GunakanStatusCodePages dengan string format

Untuk mengkustomisasi tipe konten respons dan teks, gunakan kelebihan beban UseStatusCodePages yang mengambil tipe konten dan format string:

app.UseStatusCodePages(
    "text/plain", "Status code page, status code: {0}");

GunakanStatusCodePages dengan lambda

Untuk menentukan penanganan kesalahan kustom dan kode penulisan respons, gunakan kelebihan beban UseStatusCodePages yang mengambil ekspresi lambda:

app.UseStatusCodePages(async context =>
{
    context.HttpContext.Response.ContentType = "text/plain";

    await context.HttpContext.Response.WriteAsync(
        "Status code page, status code: " + 
        context.HttpContext.Response.StatusCode);
});

GunakanStatusCodePagesWithRedirects

Metode UseStatusCodePagesWithRedirects ekstensi:

  • Mengirim kode status 302 - Ditemukan ke klien.
  • Mengalihkan klien ke lokasi yang disediakan dalam templat URL.
app.UseStatusCodePagesWithRedirects("/StatusCode?code={0}");

Templat URL dapat menyertakan {0} tempat penampung untuk kode status, seperti yang ditunjukkan dalam contoh. Jika templat URL dimulai dengan ~ (tilde), ~ digantikan oleh aplikasi PathBase. Jika Anda mengarahkan ke titik akhir dalam aplikasi, buat tampilan atau Razor halaman MVC untuk titik akhir. Razor Untuk contoh Halaman, lihat Pages/StatusCode.cshtml di aplikasi sampel.

Metode ini umumnya digunakan saat aplikasi:

  • Harus mengalihkan klien ke titik akhir yang berbeda, biasanya dalam kasus di mana aplikasi yang berbeda memproses kesalahan. Untuk aplikasi web, bilah alamat browser klien mencerminkan titik akhir yang dialihkan.
  • Tidak boleh mempertahankan dan mengembalikan kode status asli dengan respons pengalihan awal.

GunakanStatusCodePagesWithReExecute

Metode UseStatusCodePagesWithReExecute ekstensi:

  • Mengembalikan kode status asli ke klien.
  • Menghasilkan isi respons dengan mengeksekusi ulang alur permintaan menggunakan jalur alternatif.
app.UseStatusCodePagesWithReExecute("/StatusCode","?code={0}");

Jika Anda mengarahkan ke titik akhir dalam aplikasi, buat tampilan atau Razor halaman MVC untuk titik akhir. Pastikan UseStatusCodePagesWithReExecute ditempatkan sebelumnya UseRouting sehingga permintaan dapat dialihkan ke halaman status. Razor Untuk contoh Halaman, lihat Pages/StatusCode.cshtml di aplikasi sampel.

Metode ini umumnya digunakan ketika aplikasi harus:

  • Proses permintaan tanpa mengalihkan ke titik akhir yang berbeda. Untuk aplikasi web, bilah alamat browser klien mencerminkan titik akhir yang awalnya diminta.
  • Pertahankan dan kembalikan kode status asli dengan respons.

URL dan templat string kueri dapat menyertakan tempat penampung ({0}) untuk kode status. Templat URL harus dimulai dengan garis miring (/). Saat menggunakan tempat penampung di jalur, konfirmasikan bahwa titik akhir (halaman atau pengontrol) dapat memproses segmen jalur. Misalnya, Razor Halaman untuk kesalahan harus menerima nilai segmen jalur opsional dengan direktif @page :

@page "{code?}"

Titik akhir yang memproses kesalahan bisa mendapatkan URL asli yang menghasilkan kesalahan, seperti yang ditunjukkan dalam contoh berikut:

var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature != null)
{
    OriginalURL =
        statusCodeReExecuteFeature.OriginalPathBase
        + statusCodeReExecuteFeature.OriginalPath
        + statusCodeReExecuteFeature.OriginalQueryString;
}

Jangan tandai metode tindakan penangan kesalahan dengan atribut metode HTTP, seperti HttpGet. Kata kerja eksplisit mencegah beberapa permintaan mencapai metode . Izinkan akses anonim ke metode jika pengguna yang tidak diaturentikasi akan melihat tampilan kesalahan.

Menonaktifkan halaman kode status

Untuk menonaktifkan halaman kode status untuk pengontrol MVC atau metode tindakan, gunakan [SkipStatusCodePages] atribut .

Untuk menonaktifkan halaman kode status untuk permintaan tertentu dalam Razor metode handler Pages atau di pengontrol MVC, gunakan IStatusCodePagesFeature:

var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();

if (statusCodePagesFeature != null)
{
    statusCodePagesFeature.Enabled = false;
}

Kode penanganan pengecualian

Kode dalam halaman penanganan pengecualian dapat melemparkan pengecualian. Sering kali merupakan ide yang baik untuk halaman kesalahan produksi terdiri dari konten statis murni.

Header respons

Setelah header untuk respons dikirim:

  • Aplikasi tidak dapat mengubah kode status respons.
  • Halaman pengecualian atau handler apa pun tidak dapat dijalankan. Respons harus diselesaikan atau koneksi dibatalkan.

Penanganan pengecualian server

Selain logika penanganan pengecualian di aplikasi Anda, implementasi server HTTP dapat menangani beberapa pengecualian. Jika server menangkap pengecualian sebelum header respons dikirim, server mengirimkan respons 500 - Kesalahan Server Internal tanpa isi respons. Jika server menangkap pengecualian setelah header respons dikirim, server menutup koneksi. Permintaan yang tidak ditangani oleh aplikasi Anda ditangani oleh server. Pengecualian apa pun yang terjadi ketika server menangani permintaan ditangani oleh penanganan pengecualian server. Halaman kesalahan kustom aplikasi, middleware penanganan pengecualian, dan filter tidak memengaruhi perilaku ini.

Penanganan pengecualian startup

Hanya lapisan hosting yang dapat menangani pengecualian yang terjadi selama startup aplikasi. Host dapat dikonfigurasi untuk menangkap kesalahan startup dan menangkap kesalahan terperinci.

Lapisan hosting dapat menampilkan halaman kesalahan untuk kesalahan startup yang diambil hanya jika kesalahan terjadi setelah alamat host/pengikatan port. Jika pengikatan gagal:

  • Lapisan hosting mencatat pengecualian penting.
  • Proses dotnet mengalami crash.
  • Tidak ada halaman kesalahan yang ditampilkan ketika server HTTP adalah Kestrel.

Saat berjalan di IIS (atau Azure App Service) atau IIS Express, 502.5 - Kegagalan Proses dikembalikan oleh Modul Inti ASP.NET jika proses tidak dapat dimulai. Untuk informasi selengkapnya, lihat Memecahkan masalah ASP.NET Core di Azure App Service dan IIS.

Halaman kesalahan database

Middleware Halaman Kesalahan Database menangkap pengecualian terkait database yang dapat diselesaikan dengan menggunakan migrasi Kerangka Kerja Entitas. Ketika pengecualian ini terjadi, respons HTML dengan detail kemungkinan tindakan untuk menyelesaikan masalah dihasilkan. Halaman ini harus diaktifkan hanya di lingkungan Pengembangan. Aktifkan halaman dengan menambahkan kode ke Startup.Configure:

if (env.IsDevelopment())
{
    app.UseDatabaseErrorPage();
}

UseDatabaseErrorPagememerlukan paket NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.

Filter pengecualian

Di aplikasi MVC, filter pengecualian dapat dikonfigurasi secara global atau berdasarkan per pengontrol atau per tindakan. Di Razor aplikasi Pages, aplikasi tersebut dapat dikonfigurasi secara global atau per model halaman. Filter ini menangani pengecualian yang tidak tertangani yang terjadi selama eksekusi tindakan pengontrol atau filter lain. Untuk informasi selengkapnya, lihat Filter di ASP.NET Core.

Tip

Filter pengecualian berguna untuk menjebak pengecualian yang terjadi dalam tindakan MVC, tetapi tidak fleksibel seperti Middleware Penanganan Pengecualian. Sebaiknya gunakan middleware. Gunakan filter hanya di mana Anda perlu melakukan penanganan kesalahan secara berbeda berdasarkan tindakan MVC mana yang dipilih.

Kesalahan status model

Untuk informasi tentang cara menangani kesalahan status model, lihat Pengikatan model dan Validasi model.

Sumber Daya Tambahan: