ตรวจจับข้อยกเว้นที่ไม่สามารถจัดการได้ทั้งหมดใน ASP.NET Web Api


113

ฉันจะตรวจจับข้อยกเว้นที่ไม่ได้จัดการทั้งหมดที่เกิดขึ้นใน ASP.NET Web Api เพื่อให้สามารถบันทึกได้อย่างไร

จนถึงตอนนี้ฉันได้ลอง:

  • สร้างและลงทะเบียนไฟล์ ExceptionHandlingAttribute
  • ใช้Application_ErrorวิธีการในGlobal.asax.cs
  • ติดตาม AppDomain.CurrentDomain.UnhandledException
  • ติดตาม TaskScheduler.UnobservedTaskException

ExceptionHandlingAttributeประสบความสำเร็จจัดการข้อยกเว้นที่จะโยนภายในวิธีการดำเนินการควบคุมและตัวกรองการกระทำ แต่ข้อยกเว้นอื่น ๆ จะไม่จัดการตัวอย่างเช่น:

  • ข้อยกเว้นเกิดขึ้นเมื่อIQueryableส่งคืนโดยวิธีการดำเนินการไม่สามารถดำเนินการได้
  • ข้อยกเว้นที่ส่งโดยตัวจัดการข้อความ (เช่นHttpConfiguration.MessageHandlers)
  • เกิดข้อยกเว้นเมื่อสร้างอินสแตนซ์คอนโทรลเลอร์

โดยทั่วไปหากข้อยกเว้นจะทำให้เกิดข้อผิดพลาด 500 Internal Server เพื่อส่งกลับไปยังไคลเอนต์ฉันต้องการให้เข้าสู่ระบบ การนำไปใช้งานApplication_Errorนี้ทำได้ดีใน Web Forms และ MVC - ฉันจะใช้อะไรใน Web Api ได้บ้าง


คุณได้ลองใช้ASP.NET Health Monitoring แล้วหรือยัง? เพียงเปิดใช้งานและดูว่าข้อยกเว้นของคุณไม่ได้บันทึกลงในบันทึกเหตุการณ์หรือไม่
John Saunders

การตรวจสอบความสมบูรณ์ตรวจจับข้อยกเว้นไปป์ไลน์ MVC ของฉัน แต่ไม่ใช่ข้อยกเว้นไปป์ไลน์ Web Api ของฉัน
Joe Daley

ขอบคุณ - ฉันใช้เวลาสักพักในการคิดว่าทำไมฉันถึงไม่สามารถบันทึกปัญหาการสร้างตัวสร้าง / การพึ่งพาการฉีดซึ่งฉันคิดว่าฉันมีการบันทึก WebAPI ที่เรียงลำดับแล้ว ...
Overflew

คำตอบ:


156

ตอนนี้สามารถทำได้ด้วย WebAPI 2.1 (ดูมีอะไรใหม่ ):

สร้างการใช้งาน IExceptionLogger อย่างน้อยหนึ่งรายการ ตัวอย่างเช่น:

public class TraceExceptionLogger : ExceptionLogger
{
    public override void Log(ExceptionLoggerContext context)
    {
        Trace.TraceError(context.ExceptionContext.Exception.ToString());
    }
}

จากนั้นลงทะเบียนด้วย HttpConfiguration ของแอปพลิเคชันของคุณภายในการเรียกกลับ config ดังนี้:

config.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());

หรือโดยตรง:

GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());

7
@NeilBarnwell ใช่เว็บ API 2.1 สอดคล้องกับการชุมนุม System.Web.Http เวอร์ชัน5.1.0 ดังนั้นคุณต้องใช้เวอร์ชันนี้หรือสูงกว่าเพื่อใช้โซลูชันที่อธิบายไว้ที่นี่ ดูเวอร์ชันแพ็กเกจ nuget
ธันวาคม

2
ข้อผิดพลาดบางอย่าง 500 ข้อยังคงไม่ถูกจับโดยสิ่งนี้เช่น HttpException - โฮสต์ระยะไกลปิดการเชื่อมต่อ ยังมีที่สำหรับ global.asax Application_Error เพื่อจัดการข้อผิดพลาดภายนอกการประมวลผล web api หรือไม่
Avner

11
ฉันชอบความละเอียดของ doco อย่างเป็นทางการใน msdn และสิ่งที่ 99% ของนักพัฒนาต้องการจริงๆคือโค้ด 8 บรรทัดเพื่อบันทึกข้อผิดพลาด
Rocklan

20

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

อย่างไรก็ตามฉันใช้แอสเซมบลี v5.2.3 และExceptionHandlerคลาสไม่มีHandleCoreเมธอด Handleเทียบเท่าผมคิดว่าเป็น อย่างไรก็ตามการคลาสย่อยเพียงอย่างExceptionHandlerเดียว (ตามคำตอบของ Yuval) ไม่ได้ผล ในกรณีของฉันฉันต้องดำเนินการIExceptionHandlerดังนี้

internal class OopsExceptionHandler : IExceptionHandler
{
    private readonly IExceptionHandler _innerHandler;

    public OopsExceptionHandler (IExceptionHandler innerHandler)
    {
        if (innerHandler == null)
            throw new ArgumentNullException(nameof(innerHandler));

        _innerHandler = innerHandler;
    }

    public IExceptionHandler InnerHandler
    {
        get { return _innerHandler; }
    }

    public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
    {
        Handle(context);

        return Task.FromResult<object>(null);
    }

    public void Handle(ExceptionHandlerContext context)
    {
        // Create your own custom result here...
        // In dev, you might want to null out the result
        // to display the YSOD.
        // context.Result = null;
        context.Result = new InternalServerErrorResult(context.Request);
    }
}

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

config.Services.Replace(typeof(IExceptionHandler),
    new OopsExceptionHandler(config.Services.GetExceptionHandler()));

1
นี่เป็นวิธีแก้ปัญหาที่ยอดเยี่ยมซึ่งควรเป็นโซลูชันที่ได้รับการยอมรับในการ "ตรวจจับหรือบันทึกข้อผิดพลาดทั้งหมด" ฉันไม่เคยรู้เลยว่าทำไมมันถึงไม่ได้ผลสำหรับฉันเมื่อฉันแค่ขยาย ExceptionHandler
Rajiv

ทางออกที่ดี เมื่อโหลด MVC ไปป์ไลน์สำหรับคำขอแล้วจะใช้งานได้ดี IIS ยังคงจัดการข้อยกเว้นจนถึงตอนนั้นรวมถึงเมื่อหมุน OWIN ใน startup.cs อย่างไรก็ตามในบางจุดหลังจากการหมุนเสร็จสิ้นการประมวลผล startup.cs มันทำงานได้อย่างยอดเยี่ยม
Gustyn

18

เพื่อตอบคำถามของตัวเองนี่เป็นไปไม่ได้!

การจัดการข้อยกเว้นทั้งหมดที่ทำให้เกิดข้อผิดพลาดภายในเซิร์ฟเวอร์ดูเหมือนว่า Web API ควรมีความสามารถพื้นฐานดังนั้นฉันจึงส่งคำขอกับ Microsoft สำหรับตัวจัดการข้อผิดพลาดส่วนกลางสำหรับ Web API :

https://aspnetwebstack.codeplex.com/workitem/1001

ถ้าคุณเห็นด้วยไปที่ลิงค์นั้นและโหวตให้!

ในระหว่างนี้บทความที่ยอดเยี่ยมการจัดการข้อยกเว้น ASP.NET Web APIจะแสดงวิธีต่างๆในการตรวจจับข้อผิดพลาดประเภทต่างๆ มันซับซ้อนกว่าที่ควรจะเป็นและไม่พบข้อผิดพลาดของเซิร์ฟเวอร์ระหว่างกันทั้งหมดแต่เป็นแนวทางที่ดีที่สุดในปัจจุบัน

อัปเดต:ขณะนี้การจัดการข้อผิดพลาดทั่วโลกได้รับการใช้งานแล้วและพร้อมใช้งานในงานสร้างทุกคืน! จะออกใน ASP.NET MVC v5.1 นี่คือวิธีการทำงาน: https://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling


ดูเหมือนว่าจะมีเหตุผลที่ต้องใช้ตัวควบคุมสำหรับการโทร ajax แทน web api .. ขอบเขตนั้นพร่ามัวไปแล้ว .. แม้ว่า ELMAH จะสามารถจับภาพได้ แต่ก็อาจมีวิธี
Sonic Soul

4
เพิ่มการจัดการข้อผิดพลาดส่วนกลางใน Web API 2.1 แล้ว ดูคำตอบของฉันสำหรับรายละเอียดเพิ่มเติม
ประกาศ

10

คุณยังสามารถสร้างตัวจัดการข้อยกเว้นส่วนกลางได้โดยใช้IExceptionHandlerอินเทอร์เฟซ (หรือสืบทอดExceptionHandlerคลาสพื้นฐาน) จะเป็นครั้งสุดท้ายที่จะถูกเรียกในห่วงโซ่การดำเนินการหลังจากลงทะเบียนทั้งหมดIExceptionLogger:

IExceptionHandler จัดการกับข้อยกเว้นที่ไม่สามารถจัดการได้ทั้งหมดจากคอนโทรลเลอร์ทั้งหมด นี่คือรายการสุดท้ายในรายการ หากมีข้อยกเว้นเกิดขึ้น IExceptionLogger จะถูกเรียกก่อนจากนั้นจึงเรียกตัวควบคุม ExceptionFilters และหากยังไม่สามารถจัดการได้การใช้งาน IExceptionHandler

public class OopsExceptionHandler : ExceptionHandler
{
    public override void HandleCore(ExceptionHandlerContext context)
    {
        context.Result = new TextPlainErrorResult
        {
            Request = context.ExceptionContext.Request,
            Content = "Oops! Sorry! Something went wrong."        
        };
    }

    private class TextPlainErrorResult : IHttpActionResult
    {
        public HttpRequestMessage Request { get; set; }

        public string Content { get; set; }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            HttpResponseMessage response = 
                             new HttpResponseMessage(HttpStatusCode.InternalServerError);
            response.Content = new StringContent(Content);
            response.RequestMessage = Request;
            return Task.FromResult(response);
        }
    }
}

เพิ่มเติมเกี่ยวกับที่นี่


แค่อยากรู้ว่าทะเบียนนี้อยู่ที่ไหน?
ThunD3eR

-1

คุณอาจมีบล็อกลองจับที่คุณไม่ทราบอยู่แล้ว

ฉันคิดว่าglobal.asax.Application_Errorวิธีการใหม่ของฉันไม่ได้ถูกเรียกอย่างสม่ำเสมอสำหรับข้อยกเว้นที่ไม่สามารถจัดการได้ในรหัสเดิมของเรา

จากนั้นฉันก็พบบล็อก try-catch สองสามอันที่อยู่ตรงกลางของ call stack ที่เรียกว่า Response เขียนบนข้อความ Exception นั่นแหล่ะ ทิ้งข้อความบนหน้าจอแล้วฆ่าหินยกเว้นตาย

ดังนั้นจึงมีการจัดการข้อยกเว้น แต่การจัดการกลับไม่มีประโยชน์ เมื่อฉันลบบล็อก try-catch เหล่านั้นข้อยกเว้นที่เผยแพร่ไปยังเมธอด Application_Error ตามที่คาดไว้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.