Web API'ASP.NET Core hataları işleme

Bu makalede web API'leriyle hata işlemeyi nasıl ASP.NET Core ve özelleştirebileceğiniz açıklanmıştır.

Örnek kodu görüntüleme veya indirme (İndirme)

Geliştirici Özel Durum Sayfası

Geliştirici Özel Durum Sayfası, sunucu hatalarına ilişkin ayrıntılı yığın izlemeleri almak için kullanışlı bir araçtır. HTTP işlem hattından zaman uyumlu ve zaman uyumsuz özel durumları yakalamak ve hata DeveloperExceptionPageMiddleware yanıtları oluşturmak için kullanır. Göstermek için aşağıdaki denetleyici eylemlerini göz önünde kullanın:

[HttpGet("{city}")]
public WeatherForecast Get(string city)
{
    if (!string.Equals(city?.TrimEnd(), "Redmond", StringComparison.OrdinalIgnoreCase))
    {
        throw new ArgumentException(
            $"We don't offer a weather forecast for {city}.", nameof(city));
    }
    
    return GetWeather().First();
}

Önceki eylemi curl test etmek için aşağıdaki komutu çalıştırın:

curl -i https://localhost:5001/weatherforecast/chicago

Bu ASP.NET Core 3.0 ve sonraki bir sürümünün ardından, istemci HTML biçimli çıkış isteğinde bulunsa bile Geliştirici Özel Durum Sayfası düz metin yanıtı görüntüler. Aşağıdaki çıkış görüntülenir:

HTTP/1.1 500 Internal Server Error
Transfer-Encoding: chunked
Content-Type: text/plain
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Date: Fri, 27 Sep 2019 16:13:16 GMT

System.ArgumentException: We don't offer a weather forecast for chicago. (Parameter 'city')
   at WebApiSample.Controllers.WeatherForecastController.Get(String city) in C:\working_folder\aspnet\AspNetCore.Docs\aspnetcore\web-api\handle-errors\samples\3.x\Controllers\WeatherForecastController.cs:line 34
   at lambda_method(Closure , Object , Object[] )
   at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

HEADERS
=======
Accept: */*
Host: localhost:44312
User-Agent: curl/7.55.1

Bunun yerine HTML biçimli bir yanıt görüntülemek için, HTTP isteği Accept üst bilgisini medya text/html türüne ayarlayın. Örnek:

curl -i -H "Accept: text/html" https://localhost:5001/weatherforecast/chicago

HTTP yanıttan aşağıdaki alıntıyı düşünün:

2 ASP.NET Core 2.2 ve önceki sürümlerde, Geliştirici Özel Durum Sayfası HTML biçimli bir yanıt görüntüler. Örneğin, HTTP yanıttan aşağıdaki alıntıyı düşünün:

HTTP/1.1 500 Internal Server Error
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Date: Fri, 27 Sep 2019 16:55:37 GMT

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="utf-8" />
        <title>Internal Server Error</title>
        <style>
            body {
    font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif;
    font-size: .813em;
    color: #222;
    background-color: #fff;
}

HTML biçimli yanıt, Postman gibi araçlar aracılığıyla test etme sırasında yararlı olur. Aşağıdaki ekran yakalama, Postman'de hem düz metin hem de HTML biçimli yanıtları gösterir:

Postman'de Geliştirici Özel Durum Sayfası testi

Uyarı

Geliştirici Özel Durum Sayfası'nın yalnızca uygulama Geliştirme ortamında çalışıyor olduğunda etkinleştirin. Uygulama üretimde çalıştırıldıklarında ayrıntılı özel durum bilgilerini herkese açık şekilde paylaşmayın. Ortamları yapılandırma hakkında daha fazla bilgi için bkz. ASP.NET Core'da birden çok ortam kullanma .

Hata işleyici eylem yöntemini gibi HTTP yöntemi öznitelikleriyle HttpGet işaretlemeyin. Açık fiiller bazı isteklerin eylem yöntemine ulaşmasını önler. Kimliği doğrulanmamış kullanıcıların hatayı görmeleri gerekirse yöntemine anonim erişime izin ver.

Özel durum işleyicisi

Geliştirme dışı ortamlarda, hata yükü üretmek için Özel Durum İşleme Ara Yazılımı kullanılabilir:

  1. içinde Startup.Configure ara yazılımı kullanmak için UseExceptionHandler çağır:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/error");
        }
    
        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
    
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/error");
            app.UseHsts();
        }
    
        app.UseHttpsRedirection();
        app.UseMvc();
    }
    
  2. Rotaya yanıt vermek için bir denetleyici eylemi /error yapılandırma:

    [ApiController]
    public class ErrorController : ControllerBase
    {
        [Route("/error")]
        public IActionResult Error() => Problem();
    }
    
    [ApiController]
    public class ErrorController : ControllerBase
    {
        [Route("/error")]
        public ActionResult Error([FromServices] IHostingEnvironment webHostEnvironment)
        {
            var feature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
            var ex = feature?.Error;
            var isDev = webHostEnvironment.IsDevelopment();
            var problemDetails = new ProblemDetails
            {
                Status = (int)HttpStatusCode.InternalServerError,
                Instance = feature?.Path,
                Title = isDev ? $"{ex.GetType().Name}: {ex.Message}" : "An error occurred.",
                Detail = isDev ? ex.StackTrace : null,
            };
    
            return StatusCode(problemDetails.Status.Value, problemDetails);
        }
    }
    

Yukarıdaki Error eylem, istemciye RFC 7807uyumlu bir yük gönderir.

Özel Durum İşleme Ara Yazılımı, yerel geliştirme ortamında daha ayrıntılı içerik anlaşması yapılan çıkış da sağlar. Geliştirme ve üretim ortamlarında tutarlı bir yük biçimi üretmek için aşağıdaki adımları kullanın:

  1. içinde, Startup.Configure ortama özgü Özel Durum İşleme Ara Yazılım örneklerini kaydetme:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseExceptionHandler("/error-local-development");
        }
        else
        {
            app.UseExceptionHandler("/error");
        }
    }
    
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseExceptionHandler("/error-local-development");
        }
        else
        {
            app.UseExceptionHandler("/error");
        }
    }
    

    Yukarıdaki kodda ara yazılım şu şekilde kaydedilir:

    • Geliştirme /error-local-development ortamındaki bir yolu.
    • Geliştirme olmayan /error ortamlardaki bir yolu.

  2. Denetleyici eylemlerine öznitelik yönlendirmesi uygulama:

    [ApiController]
    public class ErrorController : ControllerBase
    {
        [Route("/error-local-development")]
        public IActionResult ErrorLocalDevelopment(
            [FromServices] IWebHostEnvironment webHostEnvironment)
        {
            if (webHostEnvironment.EnvironmentName != "Development")
            {
                throw new InvalidOperationException(
                    "This shouldn't be invoked in non-development environments.");
            }
    
            var context = HttpContext.Features.Get<IExceptionHandlerFeature>();
    
            return Problem(
                detail: context.Error.StackTrace,
                title: context.Error.Message);
        }
    
        [Route("/error")]
        public IActionResult Error() => Problem();
    }
    
    [ApiController]
    public class ErrorController : ControllerBase
    {
        [Route("/error-local-development")]
        public IActionResult ErrorLocalDevelopment(
            [FromServices] IHostingEnvironment webHostEnvironment)
        {
            if (!webHostEnvironment.IsDevelopment())
            {
                throw new InvalidOperationException(
                    "This shouldn't be invoked in non-development environments.");
            }
    
            var feature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
            var ex = feature?.Error;
    
            var problemDetails = new ProblemDetails
            {
                Status = (int)HttpStatusCode.InternalServerError,
                Instance = feature?.Path,
                Title = ex.GetType().Name,
                Detail = ex.StackTrace,
            };
    
            return StatusCode(problemDetails.Status.Value, problemDetails);
        }
    
        [Route("/error")]
        public ActionResult Error(
            [FromServices] IHostingEnvironment webHostEnvironment)
        {
            var feature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
            var ex = feature?.Error;
            var isDev = webHostEnvironment.IsDevelopment();
            var problemDetails = new ProblemDetails
            {
                Status = (int)HttpStatusCode.InternalServerError,
                Instance = feature?.Path,
                Title = isDev ? $"{ex.GetType().Name}: {ex.Message}" : "An error occurred.",
                Detail = isDev ? ex.StackTrace : null,
            };
    
            return StatusCode(problemDetails.Status.Value, problemDetails);
        }
    }
    

    Yukarıdaki kod, bir yanıt oluşturmak için ControllerBase.Problem'i ProblemDetails çağırıyor.

Yanıtı değiştirmek için özel durumları kullanma

Yanıtın içeriği denetleyicinin dışından değiştirilebilir. Bu ASP.NET 4.x Web API'sinde bunu yapma yollarından biri türünü HttpResponseException kullanmaktır. ASP.NET Core eşdeğer bir tür içermez. desteği HttpResponseException aşağıdaki adımlarla eklenebilir:

  1. adlı iyi bilinen bir özel durum türü HttpResponseException oluşturun:

    public class HttpResponseException : Exception
    {
        public int Status { get; set; } = 500;
    
        public object Value { get; set; }
    }
    
  2. adlı bir eylem filtresi HttpResponseExceptionFilter oluşturun:

    public class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter
    {
        public int Order { get; } = int.MaxValue - 10;
    
        public void OnActionExecuting(ActionExecutingContext context) { }
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
            if (context.Exception is HttpResponseException exception)
            {
                context.Result = new ObjectResult(exception.Value)
                {
                    StatusCode = exception.Status,
                };
                context.ExceptionHandled = true;
            }
        }
    }
    

    Yukarıdaki filtre, maksimum tamsayı Order değerinin eksi 10 değerini belirtir. Bu işlem hattının sonunda diğer filtrelerin çalışmasına olanak sağlar.

  3. içinde, Startup.ConfigureServices eylem filtresini filtreler koleksiyonuna ekleyin:

    services.AddControllers(options =>
        options.Filters.Add(new HttpResponseExceptionFilter()));
    
    services.AddMvc(options =>
            options.Filters.Add(new HttpResponseExceptionFilter()))
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    
    services.AddMvc(options =>
            options.Filters.Add(new HttpResponseExceptionFilter()))
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    

Doğrulama hatası hata yanıtı

Web API'si denetleyicileri için MVC, model doğrulama ValidationProblemDetails başarısız olduğunda yanıt türüyle yanıt verir. MVC, doğrulama hatası InvalidModelStateResponseFactory için hata yanıtını oluşturmak için sonuçlarını kullanır. Aşağıdaki örnek, içinde varsayılan yanıt türünü olarak değiştirmek için fabrikayı SerializableError Startup.ConfigureServices kullanır:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.InvalidModelStateResponseFactory = context =>
        {
            var result = new BadRequestObjectResult(context.ModelState);

            // TODO: add `using System.Net.Mime;` to resolve MediaTypeNames
            result.ContentTypes.Add(MediaTypeNames.Application.Json);
            result.ContentTypes.Add(MediaTypeNames.Application.Xml);

            return result;
        };
    });
services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .ConfigureApiBehaviorOptions(options =>
    {
        options.InvalidModelStateResponseFactory = context =>
        {
            var result = new BadRequestObjectResult(context.ModelState);

            // TODO: add `using System.Net.Mime;` to resolve MediaTypeNames
            result.ContentTypes.Add(MediaTypeNames.Application.Json);
            result.ContentTypes.Add(MediaTypeNames.Application.Xml);

            return result;
        };
    });
services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = context =>
    {
        var result = new BadRequestObjectResult(context.ModelState);

        // TODO: add `using using System.Net.Mime;` to resolve MediaTypeNames
        result.ContentTypes.Add(MediaTypeNames.Application.Json);
        result.ContentTypes.Add(MediaTypeNames.Application.Xml);

        return result;
    };
});

İstemci hatası yanıtı

Hata sonucu, HTTP durum kodu 400 veya daha yüksek olan bir sonuç olarak tanımlanır. MVC, web API'si denetleyicileri için hata sonucundan ile bir sonuç ProblemDetails döndürür.

Önemli

ASP.NET Core 2.1, neredeyse RFC 7807 uyumlu bir sorun ayrıntıları yanıtı üretir. Yüzde 100 uyumluluk önemli ise projeyi 2.2 veya ASP.NET Core sürümüne yükseltin.

Hata yanıtı aşağıdaki yöntemlerden birini yapılandırabilirsiniz:

  1. ProblemDetailsFactory Uygulama
  2. ApiBehaviorOptions.ClientErrorMapping kullanma

Uygulamak ProblemDetailsFactory

MVC, Microsoft.AspNetCore.Mvc.Infrastructure.ProblemDetailsFactory tüm ve örneklerini üretmek için ProblemDetails ValidationProblemDetails kullanır. Buna istemci hatası yanıtları, doğrulama hatası yanıtları ve ControllerBase.Problem ve ControllerBase.ValidationProblem yardımcı yöntemleri dahildir.

Sorun ayrıntıları yanıtını özelleştirmek için içinde özel bir uygulamasını ProblemDetailsFactory Startup.ConfigureServices kaydettirin:

public void ConfigureServices(IServiceCollection serviceCollection)
{
    services.AddControllers();
    services.AddTransient<ProblemDetailsFactory, CustomProblemDetailsFactory>();
}

Hata yanıtı ApiBehaviorOptions.ClientErrorMapping kullanma bölümünde açıklanan şekilde yalıtılmış olabilir.

ApiBehaviorOptions.ClientErrorMapping kullanma

Yanıtın ClientErrorMapping içeriğini yapılandırmak için özelliğini ProblemDetails kullanın. Örneğin, içinde aşağıdaki kod Startup.ConfigureServices type 404 yanıtları için özelliğini günceller:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });
services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[404].Link =
            "https://httpstatuses.com/404";
    });

Özel durumları işlemek için Özel Ara Yazılım

Özel durum işleme ara yazılımında varsayılanlar çoğu uygulama için iyi çalışır. Özelleştirilmiş özel durum işleme gerektiren uygulamalar için özel durum işleme ara yazılımlarını özelleştirmeyi göz önünde bulundurabilirsiniz.

Özel durumlar için ProblemDetails yükü üretme

ASP.NET Core, sunucu işlanmamış bir özel durumla karşılaştığında standartlaştırılmış bir hata yükü üretmez. İstemciye standartlaştırılmış bir ProblemDetails yanıtının geri dönmesi istenen senaryolarda, özel durumları ve 404'leri bir ProblemDetails yüküyle eşlemek için ProblemDetails ara yazılımı kullanılabilir. Özel durum işleme ara yazılımı, işlanmamış özel durumlar ProblemDetails için bir yük dönmek için de kullanılabilir.