การจัดการข้อยกเว้น ASP.NET Core Web API


280

ฉันใช้ ASP.NET Core สำหรับโครงการ REST API ใหม่หลังจากใช้ ASP.NET Web API ปกติเป็นเวลาหลายปี ฉันไม่เห็นวิธีที่ดีในการจัดการข้อยกเว้นใน ASP.NET Core Web API ฉันพยายามใช้ตัวจัดการข้อยกเว้น / คุณสมบัติ:

public class ErrorHandlingFilter : ExceptionFilterAttribute
{
    public override void OnException(ExceptionContext context)
    {
        HandleExceptionAsync(context);
        context.ExceptionHandled = true;
    }

    private static void HandleExceptionAsync(ExceptionContext context)
    {
        var exception = context.Exception;

        if (exception is MyNotFoundException)
            SetExceptionResult(context, exception, HttpStatusCode.NotFound);
        else if (exception is MyUnauthorizedException)
            SetExceptionResult(context, exception, HttpStatusCode.Unauthorized);
        else if (exception is MyException)
            SetExceptionResult(context, exception, HttpStatusCode.BadRequest);
        else
            SetExceptionResult(context, exception, HttpStatusCode.InternalServerError);
    }

    private static void SetExceptionResult(
        ExceptionContext context, 
        Exception exception, 
        HttpStatusCode code)
    {
        context.Result = new JsonResult(new ApiResponse(exception))
        {
            StatusCode = (int)code
        };
    }
}

และนี่คือการลงทะเบียนตัวกรองเริ่มต้นของฉัน:

services.AddMvc(options =>
{
    options.Filters.Add(new AuthorizationFilter());
    options.Filters.Add(new ErrorHandlingFilter());
});

ปัญหาที่ผมมีคือว่าเมื่อ occurres ยกเว้นในของฉันมันไม่ได้ถูกจัดการโดยAuthorizationFilter ErrorHandlingFilterฉันคาดหวังว่ามันจะถูกจับที่นั่นเหมือนกับที่ทำงานกับ ASP.NET Web API เก่า

ดังนั้นฉันจะจับข้อยกเว้นแอปพลิเคชันทั้งหมดรวมถึงข้อยกเว้นใด ๆ จากตัวกรองการดำเนินการได้อย่างไร


3
คุณลองUseExceptionHandlerมิดเดิลแวร์แล้วหรือยัง
Pawel

ฉันมีตัวอย่างที่นี่เกี่ยวกับวิธีการใช้UseExceptionHandlerมิดเดิลแวร์
Ilya Chernomordik

คำตอบ:


538

การจัดการข้อยกเว้น Middleware

หลังจากการทดลองหลายครั้งด้วยวิธีการจัดการข้อยกเว้นที่แตกต่างกันฉันได้ใช้มิดเดิลแวร์ มันทำงานได้ดีที่สุดสำหรับแอปพลิเคชัน ASP.NET Core Web API ของฉัน มันจัดการข้อยกเว้นแอปพลิเคชันเช่นเดียวกับข้อยกเว้นจากตัวกรองการกระทำและฉันมีการควบคุมเต็มรูปแบบในการจัดการข้อยกเว้นและการตอบสนอง HTTP นี่คือข้อยกเว้นของฉันในการจัดการมิดเดิลแวร์:

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate next;
    public ErrorHandlingMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context /* other dependencies */)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private static Task HandleExceptionAsync(HttpContext context, Exception ex)
    {
        var code = HttpStatusCode.InternalServerError; // 500 if unexpected

        if      (ex is MyNotFoundException)     code = HttpStatusCode.NotFound;
        else if (ex is MyUnauthorizedException) code = HttpStatusCode.Unauthorized;
        else if (ex is MyException)             code = HttpStatusCode.BadRequest;

        var result = JsonConvert.SerializeObject(new { error = ex.Message });
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)code;
        return context.Response.WriteAsync(result);
    }
}

ลงทะเบียนก่อน MVCในStartupชั้นเรียน:

app.UseMiddleware(typeof(ErrorHandlingMiddleware));
app.UseMvc();

คุณสามารถเพิ่มการติดตามสแต็กชื่อประเภทข้อยกเว้นรหัสข้อผิดพลาดหรืออะไรก็ได้ที่คุณต้องการ ยืดหยุ่นมาก นี่คือตัวอย่างของการตอบสนองข้อยกเว้น:

{ "error": "Authentication token is not valid." }

ลองIOptions<MvcJsonOptions>ใช้Invokeวิธีการนั้นเมื่อคุณทำการซีเรียลออบเจ็กต์การตอบสนองเพื่อใช้ประโยชน์จากการตั้งค่าการทำให้เป็นอนุกรมของ ASP.NET MVC JsonConvert.SerializeObject(errorObj, opts.Value.SerializerSettings)เพื่อความสอดคล้องที่ดีขึ้นในทุกจุดสิ้นสุด

วิธีที่ 2

มีอีก API ที่ไม่ชัดเจนที่เรียกUseExceptionHandlerว่า "ok" สำหรับการใช้งานง่าย ๆ :

app.UseExceptionHandler(a => a.Run(async context =>
{
    var feature = context.Features.Get<IExceptionHandlerPathFeature>();
    var exception = feature.Error;

    var result = JsonConvert.SerializeObject(new { error = exception.Message });
    context.Response.ContentType = "application/json";
    await context.Response.WriteAsync(result);
}));

นี่ไม่ใช่วิธีที่ชัดเจน แต่ง่ายต่อการตั้งค่าการจัดการข้อยกเว้น อย่างไรก็ตามฉันยังคงชอบวิธีการมิดเดิลแวร์มากกว่าเพราะฉันสามารถควบคุมได้มากขึ้นด้วยความสามารถในการฉีดการพึ่งพาที่จำเป็น


4
ฉันได้ตีหัวของฉันกับโต๊ะพยายามที่จะได้รับมิดเดิลแวร์ที่กำหนดเองเพื่อทำงานในวันนี้และมันก็ทำงานในลักษณะเดียวกัน (ฉันใช้มันเพื่อจัดการหน่วยงาน / ธุรกรรมสำหรับคำขอ) ปัญหาที่ฉันเผชิญคือการยกข้อยกเว้นใน 'ถัดไป' ไม่ได้ติดอยู่ในมิดเดิลแวร์ อย่างที่คุณจินตนาการได้นี่เป็นปัญหา ฉันทำอะไรผิดหรือขาดหายไป ตัวชี้หรือคำแนะนำใด ๆ
brappleye3

5
@ brappleye3 - ฉันคิดออกว่าปัญหาคืออะไร ฉันเพิ่งลงทะเบียนมิดเดิลแวร์ในตำแหน่งที่ไม่ถูกต้องในคลาส Startup.cs ผมย้ายไปก่อนapp.UseMiddleware<ErrorHandlingMiddleware>(); app.UseStaticFiles();ดูเหมือนว่าข้อยกเว้นจะถูกต้องในขณะนี้ สิ่งนี้ทำให้ฉันเชื่อว่าapp.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); app.UseBrowserLink();ทำแฮกเกอร์มิดเดิ้ลเวทย์มนตร์ภายในเพื่อรับลำดับมิดเดิลแวร์
Jamadan

4
ฉันยอมรับว่ามิดเดิลแวร์แบบกำหนดเองนั้นมีประโยชน์มาก แต่คำถามจะใช้ข้อยกเว้นสำหรับสถานการณ์ NotFound, Unauthorized และ BadRequest ทำไมไม่ตั้งรหัสสถานะ (โดยใช้ NotFound () ฯลฯ ) แล้วจัดการกับมิดเดิลแวร์ที่คุณกำหนดเองหรือผ่านทาง UseStatusCodePagesWithReExecute ดูdevtrends.co.uk/blog/handling-errors-in-asp.net-core-web-apiสำหรับข้อมูลเพิ่มเติม
Paul Hiles

4
มันไม่ดีเพราะมันเป็นอันดับต่อเนื่องของ JSON โดยไม่สนใจการเจรจาต่อรองเนื้อหา
Konrad

5
@Konrad จุดที่ถูกต้อง นั่นเป็นเหตุผลที่ฉันพูดว่าตัวอย่างนี้เป็นที่ที่คุณสามารถเริ่มต้นได้และไม่ใช่ผลลัพธ์สุดท้าย สำหรับ 99% ของ APIs JSON นั้นมากเกินพอ หากคุณรู้สึกว่าคำตอบนี้ไม่ดีพอที่จะมีส่วนร่วม
Andrei

60

ล่าสุดAsp.Net Core(อย่างน้อยจาก 2.2 อาจจะก่อนหน้านี้) มีมิดเดิลแวร์ในตัวที่ทำให้ง่ายขึ้นเล็กน้อยเมื่อเทียบกับการใช้งานในคำตอบที่ยอมรับ:

app.UseExceptionHandler(a => a.Run(async context =>
{
    var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
    var exception = exceptionHandlerPathFeature.Error;

    var result = JsonConvert.SerializeObject(new { error = exception.Message });
    context.Response.ContentType = "application/json";
    await context.Response.WriteAsync(result);
}));

มันควรจะเหมือนกันมากแค่เขียนโค้ดให้น้อยลง

สำคัญ:อย่าลืมเพิ่มก่อนUseMvc(หรือUseRoutingใน. Net Core 3) ตามลำดับเป็นสิ่งสำคัญ


มันสนับสนุน DI ในฐานะที่เป็นตัวระบุถึงตัวจัดการหรือเราจะต้องใช้รูปแบบตัวระบุบริการภายในตัวจัดการ
lp

32

ทางออกที่ดีที่สุดของคุณคือการใช้มิดเดิลแวร์ในการบันทึกที่คุณต้องการ คุณต้องการที่จะทำให้การบันทึกข้อยกเว้นของคุณในมิดเดิลแวร์หนึ่งและจากนั้นจัดการหน้าข้อผิดพลาดของคุณแสดงให้กับผู้ใช้ในมิดเดิลแวร์ที่แตกต่างกัน ที่ช่วยให้แยกลอจิกและตามการออกแบบที่ Microsoft ได้วางไว้กับคอมโพเนนต์มิดเดิลแวร์ 2 ตัว นี่คือลิงค์ที่ดีไปยังเอกสารของ Microsoft: การจัดการข้อผิดพลาดใน ASP.Net Core

ตัวอย่างเช่นคุณโดยเฉพาะคุณอาจต้องการที่จะใช้หนึ่งของนามสกุลในตัวกลาง StatusCodePageหรือม้วนของคุณเองเช่นนี้

คุณสามารถหาตัวอย่างได้ที่นี่สำหรับการบันทึกข้อยกเว้น: ExceptionHandlerMiddleware.cs

public void Configure(IApplicationBuilder app)
{
    // app.UseErrorPage(ErrorPageOptions.ShowAll);
    // app.UseStatusCodePages();
    // app.UseStatusCodePages(context => context.HttpContext.Response.SendAsync("Handler, status code: " + context.HttpContext.Response.StatusCode, "text/plain"));
    // app.UseStatusCodePages("text/plain", "Response, status code: {0}");
    // app.UseStatusCodePagesWithRedirects("~/errors/{0}");
    // app.UseStatusCodePagesWithRedirects("/base/errors/{0}");
    // app.UseStatusCodePages(builder => builder.UseWelcomePage());
    app.UseStatusCodePagesWithReExecute("/Errors/{0}");  // I use this version

    // Exception handling logging below
    app.UseExceptionHandler();
}

หากคุณไม่ชอบการนำไปใช้เฉพาะนั้นคุณสามารถใช้ELM Middlewareและนี่คือตัวอย่าง: Elm Exception Middleware

public void Configure(IApplicationBuilder app)
{
    app.UseStatusCodePagesWithReExecute("/Errors/{0}");
    // Exception handling logging below
    app.UseElmCapture();
    app.UseElmPage();
}

หากนั่นไม่ได้ผลสำหรับความต้องการของคุณคุณสามารถม้วนส่วนประกอบ Middleware ของคุณเองได้โดยดูที่การใช้งาน ExceptionHandlerMiddleware และ ElmMiddleware เพื่อเข้าใจแนวคิดสำหรับการสร้างของคุณเอง

สิ่งสำคัญคือการเพิ่มข้อยกเว้นการจัดการมิดเดิลแวร์ที่ด้านล่างของมิดเดิลแวร์ StatusCodePages แต่เหนือกว่ามิดเดิลแวร์ส่วนประกอบทั้งหมดของคุณ วิธีที่มิดเดิลแวร์ข้อยกเว้นของคุณจะจับข้อยกเว้นให้บันทึกจากนั้นอนุญาตให้ดำเนินการร้องขอไปยังมิดเดิลแวร์ StatusCodePage ซึ่งจะแสดงหน้าข้อผิดพลาดที่เป็นมิตรกับผู้ใช้


ไม่เป็นไร ฉันยังให้ลิงก์ไปยังตัวอย่างสำหรับการแทนที่ UseStatusPages ที่เป็นค่าเริ่มต้นในกรณีขอบที่อาจตอบสนองคำขอของคุณได้ดีขึ้น
แอชลี่ย์ลี

1
โปรดทราบว่า Elm ไม่เก็บบันทึกไว้และแนะนำให้ใช้ Serilog หรือ NLog เพื่อจัดทำซีเรียลไลเซชัน ดู บันทึก ELM จะหายไป เราสามารถเก็บมันไว้ในไฟล์หรือฐานข้อมูลได้หรือไม่?
Michael Freidgeim

2
ลิงค์เสีย
Mathias Lykkegaard Lorenzen

@AshleyLee ฉันถามว่าUseStatusCodePagesมีการใช้งานในการใช้งานบริการเว็บ API ไม่มีมุมมองหรือ HTML เลยการตอบสนองของ JSON เท่านั้น ...
Paul Michalik

23

คำตอบที่ได้รับการยอมรับเป็นอย่างดีช่วยฉันได้มาก แต่ฉันต้องการส่ง HttpStatusCode ในมิดเดิลแวร์ของฉันเพื่อจัดการรหัสสถานะข้อผิดพลาดที่รันไทม์

ตามลิงค์นี้ฉันมีความคิดที่จะทำเช่นเดียวกัน ดังนั้นฉันจึงรวมคำตอบ Andrei เข้ากับสิ่งนี้ ดังนั้นรหัสสุดท้ายของฉันอยู่ด้านล่าง:
1 คลาสฐาน

public class ErrorDetails
{
    public int StatusCode { get; set; }
    public string Message { get; set; }

    public override string ToString()
    {
        return JsonConvert.SerializeObject(this);
    }
}

2. ประเภทคลาสยกเว้นที่กำหนดเอง

 public class HttpStatusCodeException : Exception
{
    public HttpStatusCode StatusCode { get; set; }
    public string ContentType { get; set; } = @"text/plain";

    public HttpStatusCodeException(HttpStatusCode statusCode)
    {
        this.StatusCode = statusCode;
    }

    public HttpStatusCodeException(HttpStatusCode statusCode, string message) : base(message)
    {
        this.StatusCode = statusCode;
    }

    public HttpStatusCodeException(HttpStatusCode statusCode, Exception inner) : this(statusCode, inner.ToString()) { }

    public HttpStatusCodeException(HttpStatusCode statusCode, JObject errorObject) : this(statusCode, errorObject.ToString())
    {
        this.ContentType = @"application/json";
    }

}


3. Middleware Exception ที่กำหนดเอง

public class CustomExceptionMiddleware
    {
        private readonly RequestDelegate next;

    public CustomExceptionMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context /* other dependencies */)
    {
        try
        {
            await next(context);
        }
        catch (HttpStatusCodeException ex)
        {
            await HandleExceptionAsync(context, ex);
        }
        catch (Exception exceptionObj)
        {
            await HandleExceptionAsync(context, exceptionObj);
        }
    }

    private Task HandleExceptionAsync(HttpContext context, HttpStatusCodeException exception)
    {
        string result = null;
        context.Response.ContentType = "application/json";
        if (exception is HttpStatusCodeException)
        {
            result = new ErrorDetails() { Message = exception.Message, StatusCode = (int)exception.StatusCode }.ToString();
            context.Response.StatusCode = (int)exception.StatusCode;
        }
        else
        {
            result = new ErrorDetails() { Message = "Runtime Error", StatusCode = (int)HttpStatusCode.BadRequest }.ToString();
            context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
        }
        return context.Response.WriteAsync(result);
    }

    private Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        string result = new ErrorDetails() { Message = exception.Message, StatusCode = (int)HttpStatusCode.InternalServerError }.ToString();
        context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
        return context.Response.WriteAsync(result);
    }
}


4. วิธีการขยาย

public static void ConfigureCustomExceptionMiddleware(this IApplicationBuilder app)
    {
        app.UseMiddleware<CustomExceptionMiddleware>();
    }

5. กำหนดค่าวิธีการใน startup.cs

app.ConfigureCustomExceptionMiddleware();
app.UseMvc();

ตอนนี้วิธีการเข้าสู่ระบบของฉันในตัวควบคุมบัญชี:

 try
        {
            IRepository<UserMaster> obj = new Repository<UserMaster>(_objHeaderCapture, Constants.Tables.UserMaster);
            var Result = obj.Get().AsQueryable().Where(sb => sb.EmailId.ToLower() == objData.UserName.ToLower() && sb.Password == objData.Password.ToEncrypt() && sb.Status == (int)StatusType.Active).FirstOrDefault();
            if (Result != null)//User Found
                return Result;
            else// Not Found
                throw new HttpStatusCodeException(HttpStatusCode.NotFound, "Please check username or password");
        }
        catch (Exception ex)
        {
            throw ex;
        }

ด้านบนคุณสามารถดูว่าฉันไม่พบผู้ใช้แล้วเพิ่ม HttpStatusCodeException ที่ฉันได้ผ่านสถานะ HttpStatusCode.NotFound และข้อความที่กำหนดเอง
ในมิดเดิลแวร์

catch (HttpStatusCodeException ex)

บล็อกจะถูกเรียกซึ่งจะผ่านการควบคุมไป

Private Task HandleExceptionAsync (บริบท HttpContext, ข้อยกเว้น HttpStatusCodeException)

.


แต่ถ้าฉันมีข้อผิดพลาด runtime มาก่อน เพื่อที่ฉันได้ใช้ลอง catch catch ซึ่งข้อยกเว้นการโยนและจะถูกจับใน catch (Exception exceptionObj) บล็อกและจะผ่านการควบคุมไป

Task HandleExceptionAsync (บริบท HttpContext, ข้อยกเว้น)

วิธี.

ฉันใช้คลาส ErrorDetails เดียวเพื่อความสม่ำเสมอ


จะใส่วิธีการขยายได้ที่ไหน? แต่น่าเสียดายที่ในstartup.csในที่ฉันได้รับข้อผิดพลาดvoid Configure(IapplicationBuilder app) IApplicationBuilder does not contain a definition for ConfigureCustomExceptionMiddlewareและฉันจะเพิ่มการอ้างอิงที่CustomExceptionMiddleware.csเป็น
Spedo De La Rossa

คุณไม่ต้องการใช้ข้อยกเว้นเนื่องจาก API ช้าลง ข้อยกเว้นมีราคาแพงมาก
lnaie

@Inaie ไม่สามารถพูดเกี่ยวกับที่ ... แต่ดูเหมือนว่าคุณไม่เคยมีข้อยกเว้นที่จะจัดการกับ .. งานที่ดี
Arjun

19

ในการกำหนดค่าพฤติกรรมการจัดการข้อยกเว้นตามประเภทข้อยกเว้นคุณสามารถใช้ Middleware จากแพ็คเกจ NuGet:

ตัวอย่างโค้ด:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddExceptionHandlingPolicies(options =>
    {
        options.For<InitializationException>().Rethrow();

        options.For<SomeTransientException>().Retry(ro => ro.MaxRetryCount = 2).NextPolicy();

        options.For<SomeBadRequestException>()
        .Response(e => 400)
            .Headers((h, e) => h["X-MyCustomHeader"] = e.Message)
            .WithBody((req,sw, exception) =>
                {
                    byte[] array = Encoding.UTF8.GetBytes(exception.ToString());
                    return sw.WriteAsync(array, 0, array.Length);
                })
        .NextPolicy();

        // Ensure that all exception types are handled by adding handler for generic exception at the end.
        options.For<Exception>()
        .Log(lo =>
            {
                lo.EventIdFactory = (c, e) => new EventId(123, "UnhandlerException");
                lo.Category = (context, exception) => "MyCategory";
            })
        .Response(null, ResponseAlreadyStartedBehaviour.GoToNextHandler)
            .ClearCacheHeaders()
            .WithObjectResult((r, e) => new { msg = e.Message, path = r.Path })
        .Handled();
    });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseExceptionHandlingPolicies();
    app.UseMvc();
}

16

ประการแรกขอบคุณอังเดรในขณะที่ฉันได้แก้ปัญหาตามตัวอย่างของเขา

ฉันรวมของฉันไว้ด้วยเพราะเป็นตัวอย่างที่สมบูรณ์มากขึ้นและอาจช่วยให้ผู้อ่านประหยัดเวลา

ข้อ จำกัด ของวิธีการของ Andrei คือไม่จัดการการบันทึกการจับตัวแปรคำขอที่มีประโยชน์และการเจรจาต่อรองเนื้อหา (มันจะส่งคืน JSON เสมอไม่ว่าลูกค้าจะขออะไร - XML ​​/ ข้อความธรรมดา ฯลฯ )

วิธีการของฉันคือการใช้ ObjectResult ซึ่งช่วยให้เราสามารถใช้ฟังก์ชั่นอบใน MVC

รหัสนี้ยังป้องกันการแคชของการตอบสนอง

การตอบสนองข้อผิดพลาดได้รับการตกแต่งในลักษณะที่สามารถทำให้เป็นอันดับโดย XML serializer

public class ExceptionHandlerMiddleware
{
    private readonly RequestDelegate next;
    private readonly IActionResultExecutor<ObjectResult> executor;
    private readonly ILogger logger;
    private static readonly ActionDescriptor EmptyActionDescriptor = new ActionDescriptor();

    public ExceptionHandlerMiddleware(RequestDelegate next, IActionResultExecutor<ObjectResult> executor, ILoggerFactory loggerFactory)
    {
        this.next = next;
        this.executor = executor;
        logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>();
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, $"An unhandled exception has occurred while executing the request. Url: {context.Request.GetDisplayUrl()}. Request Data: " + GetRequestData(context));

            if (context.Response.HasStarted)
            {
                throw;
            }

            var routeData = context.GetRouteData() ?? new RouteData();

            ClearCacheHeaders(context.Response);

            var actionContext = new ActionContext(context, routeData, EmptyActionDescriptor);

            var result = new ObjectResult(new ErrorResponse("Error processing request. Server error."))
            {
                StatusCode = (int) HttpStatusCode.InternalServerError,
            };

            await executor.ExecuteAsync(actionContext, result);
        }
    }

    private static string GetRequestData(HttpContext context)
    {
        var sb = new StringBuilder();

        if (context.Request.HasFormContentType && context.Request.Form.Any())
        {
            sb.Append("Form variables:");
            foreach (var x in context.Request.Form)
            {
                sb.AppendFormat("Key={0}, Value={1}<br/>", x.Key, x.Value);
            }
        }

        sb.AppendLine("Method: " + context.Request.Method);

        return sb.ToString();
    }

    private static void ClearCacheHeaders(HttpResponse response)
    {
        response.Headers[HeaderNames.CacheControl] = "no-cache";
        response.Headers[HeaderNames.Pragma] = "no-cache";
        response.Headers[HeaderNames.Expires] = "-1";
        response.Headers.Remove(HeaderNames.ETag);
    }

    [DataContract(Name= "ErrorResponse")]
    public class ErrorResponse
    {
        [DataMember(Name = "Message")]
        public string Message { get; set; }

        public ErrorResponse(string message)
        {
            Message = message;
        }
    }
}

9

ขั้นแรกให้กำหนดค่า ASP.NET Core 2 Startupเพื่อเรียกใช้งานอีกครั้งไปยังหน้าข้อผิดพลาดสำหรับข้อผิดพลาดใด ๆ จากเว็บเซิร์ฟเวอร์และข้อยกเว้นที่ไม่สามารถจัดการได้

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment()) {
        // Debug config here...
    } else {
        app.UseStatusCodePagesWithReExecute("/Error");
        app.UseExceptionHandler("/Error");
    }
    // More config...
}

ถัดไปกำหนดประเภทข้อยกเว้นที่จะช่วยให้คุณสามารถโยนข้อผิดพลาดด้วยรหัสสถานะ HTTP

public class HttpException : Exception
{
    public HttpException(HttpStatusCode statusCode) { StatusCode = statusCode; }
    public HttpStatusCode StatusCode { get; private set; }
}

ในที่สุดในคอนโทรลเลอร์ของคุณสำหรับหน้าข้อผิดพลาดให้ปรับแต่งการตอบสนองตามเหตุผลของข้อผิดพลาดและระบุว่าผู้ใช้ปลายทางจะเห็นการตอบสนองโดยตรงหรือไม่ รหัสนี้จะถือว่า API ทั้งหมด URL /api/ที่เริ่มต้นด้วย

[AllowAnonymous]
public IActionResult Error()
{
    // Gets the status code from the exception or web server.
    var statusCode = HttpContext.Features.Get<IExceptionHandlerFeature>()?.Error is HttpException httpEx ?
        httpEx.StatusCode : (HttpStatusCode)Response.StatusCode;

    // For API errors, responds with just the status code (no page).
    if (HttpContext.Features.Get<IHttpRequestFeature>().RawTarget.StartsWith("/api/", StringComparison.Ordinal))
        return StatusCode((int)statusCode);

    // Creates a view model for a user-friendly error page.
    string text = null;
    switch (statusCode) {
        case HttpStatusCode.NotFound: text = "Page not found."; break;
        // Add more as desired.
    }
    return View("Error", new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier, ErrorText = text });
}

ASP.NET Core จะบันทึกรายละเอียดข้อผิดพลาดเพื่อให้คุณทำการดีบักด้วยดังนั้นรหัสสถานะอาจเป็นข้อมูลทั้งหมดที่คุณต้องการให้กับผู้ร้องขอ (อาจไม่น่าเชื่อถือ) หากคุณต้องการแสดงข้อมูลเพิ่มเติมคุณสามารถปรับปรุงHttpExceptionเพื่อให้ข้อมูลได้ สำหรับข้อผิดพลาด API คุณสามารถใส่ข้อมูลผิดพลาด JSON เข้ารหัสในเนื้อหาของข้อความโดยการแทนที่ด้วยreturn StatusCode...return Json...


0

ใช้มิดเดิลแวร์หรือ IExceptionHandlerPathFeature ได้ มีอีกวิธีในeshop

สร้าง exceptionfilter และลงทะเบียน

public class HttpGlobalExceptionFilter : IExceptionFilter
{
  public void OnException(ExceptionContext context)
  {...}
}
services.AddMvc(options =>
{
  options.Filters.Add(typeof(HttpGlobalExceptionFilter));
})
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.