หน้าข้อผิดพลาดที่กำหนดเอง ASP.NET - Server.GetLastError () เป็นโมฆะ


112

ฉันมีหน้าแสดงข้อผิดพลาดที่กำหนดเองสำหรับแอปพลิเคชันของฉัน:

<customErrors mode="On" defaultRedirect="~/errors/GeneralError.aspx"
/>

ใน Global.asax, Application_Error () รหัสต่อไปนี้จะทำงานเพื่อรับรายละเอียดข้อยกเว้น:

  Exception ex = Server.GetLastError();
  if (ex != null)
    {
        if (ex.GetBaseException() != null)
            ex = ex.GetBaseException();
    }

เมื่อถึงเวลาที่ฉันไปที่หน้าข้อผิดพลาดของฉัน (~ / error / GeneralError.aspx.cs) Server.GetLastError () เป็นโมฆะ

มีวิธีใดบ้างที่ฉันจะได้รับรายละเอียดข้อยกเว้นในหน้าข้อผิดพลาดแทนที่จะเป็นใน Global.asax.cs

ASP.NET 3.5 บน Vista / IIS7


นำไปใช้กับ ASP.NET 4.0 บน Win7 ด้วย Cassini
Marcel

เพิ่ม "<customErrors mode =" RemoteOnly "defaultRedirect =" ~ / error / GeneralError.aspx "redirectMode =" ResponseRewrite "/>" เป็นคำตอบที่ได้รับการยืนยัน
elle0087

คำตอบ:


137

ดูอย่างละเอียดมากขึ้นในการตั้งค่า web.config ของฉันหนึ่งในความคิดเห็นในโพสต์นี้มีประโยชน์มาก

ใน asp.net 3.5 sp1 มีพารามิเตอร์ใหม่ redirectMode

ดังนั้นเราสามารถแก้ไขcustomErrorsเพื่อเพิ่มพารามิเตอร์นี้:

<customErrors mode="RemoteOnly" defaultRedirect="~/errors/GeneralError.aspx" redirectMode="ResponseRewrite" />

ResponseRewriteโหมดช่วยให้เราสามารถโหลด«ข้อผิดพลาดหน้า»โดยไม่ต้องเปลี่ยนเส้นทางเบราว์เซอร์เพื่อให้การเข้าพัก URL เดียวกันและที่สำคัญสำหรับฉันข้อมูลข้อยกเว้นไม่ได้หายไป


4
สิ่งนี้ไม่ได้ผลสำหรับฉัน ข้อมูลข้อยกเว้นสูญหาย ฉันจะเก็บไว้ในเซสชันใน Application_Error () และดึงกลับออกมาในตัวจัดการ Page_Load () ของหน้าข้อผิดพลาดของฉัน
BrianK

2
นั่นควรเป็นบรรทัดฐานในเอกสารทั้งหมด นี่เป็นเรื่องที่ดีมากฉันไม่เห็นเหตุผลที่จะสนับสนุนพฤติกรรมเดิม ๆ อีกต่อไป ตราบใดที่รหัสสถานะถูกต้องก็ไม่ควรมีปัญหาในการปล่อยให้ URL คำขอเดิมเหมือนเดิม (ไม่ทำการเปลี่ยนเส้นทางเบราว์เซอร์) ในความเป็นจริงนั้นถูกต้องมากขึ้นตาม HTTP เนื่องจากรหัสตอบกลับเกี่ยวข้องกับ URL ที่ร้องขอไม่ใช่คำขอหน้าข้อผิดพลาดที่ใช้ร่วมกัน ขอบคุณสำหรับตัวชี้ที่ฉันพลาดฟีเจอร์ใหม่!
Tony Wall

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

2
เนื่องจากเป็นคำตอบเก่าที่เพิ่มความคิดเห็นของฉันเพื่อพิสูจน์ว่าค่า ResponseRewrite อันดีนี้ในโหมดเปลี่ยนเส้นทางทำงานใน Asp.Net 4.5
Sundara Prabu

38

ตกลงฉันพบโพสต์นี้: http://msdn.microsoft.com/en-us/library/aa479319.aspx

ด้วยแผนภาพที่เป็นภาพประกอบนี้:

แผนภาพ
(ที่มา: microsoft.com )

โดยพื้นฐานแล้วในการรับรายละเอียดข้อยกเว้นเหล่านั้นฉันจำเป็นต้องจัดเก็บด้วยตนเองใน Global.asax สำหรับการเรียกค้นในภายหลังในหน้าข้อผิดพลาดที่กำหนดเองของฉัน

ดูเหมือนว่าวิธีที่ดีที่สุดคือการทำงานจำนวนมากใน Global.asax โดยที่หน้าข้อผิดพลาดที่กำหนดเองจะจัดการเนื้อหาที่เป็นประโยชน์มากกว่าตรรกะ


18

การรวมกันของสิ่งที่ NailItDown และ Victor พูด วิธีที่ต้องการ / ง่ายที่สุดคือใช้ Global Asax ของคุณเพื่อจัดเก็บข้อผิดพลาดจากนั้นเปลี่ยนเส้นทางไปยังหน้าข้อผิดพลาดที่คุณกำหนดเอง

Global.asax :

    void Application_Error(object sender, EventArgs e) 
{
    // Code that runs when an unhandled error occurs
    Exception ex = Server.GetLastError();
    Application["TheException"] = ex; //store the error for later
    Server.ClearError(); //clear the error so we can continue onwards
    Response.Redirect("~/myErrorPage.aspx"); //direct user to error page
}

นอกจากนี้คุณต้องตั้งค่าweb.configของคุณ:

  <system.web>
    <customErrors mode="RemoteOnly" defaultRedirect="~/myErrorPage.aspx">
    </customErrors>
  </system.web>

และสุดท้ายทำทุกอย่างที่คุณต้องการยกเว้นที่คุณเก็บไว้ในหน้าข้อผิดพลาดของคุณ:

protected void Page_Load(object sender, EventArgs e)
{

    // ... do stuff ...
    //we caught an exception in our Global.asax, do stuff with it.
    Exception caughtException = (Exception)Application["TheException"];
    //... do stuff ...
}

35
หากคุณเก็บไว้ในแอปพลิเคชันผู้ใช้อื่น ๆ ทั้งหมดของระบบจะเป็นอย่างไร มันควรจะอยู่ในเซสชั่นไม่ใช่เหรอ?
BrianK

11
นั่นเป็นวิธีการที่แย่มากในการจัดเก็บสิ่งนี้บนแอปพลิเคชัน ["TheException"]
Junior Mayhé

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

5
+1 แต่โปรดทราบว่าApplication[]เป็นวัตถุระดับโลก ในทางทฤษฎีคุณอาจมีสภาวะการแข่งขันที่หน้าที่สองเขียนทับข้อผิดพลาด อย่างไรก็ตามเนื่องจากSession[]ไม่สามารถใช้ได้เสมอภายใต้เงื่อนไขข้อผิดพลาดฉันคิดว่านี่เป็นทางเลือกที่ดีกว่า
Andomar

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

6

ลองใช้บางอย่างเช่นServer.Transfer("~/ErrorPage.aspx");จากภายในApplication_Error()วิธี global.asax.cs

จากภายในPage_Load()ErrorPage.aspx.cs คุณควรจะทำสิ่งที่ชอบ:Exception exception = Server.GetLastError().GetBaseException();

Server.Transfer() ดูเหมือนจะเก็บข้อยกเว้นไว้รอบ ๆ


นี่คือวิธีที่แอปพลิเคชันของฉันทำและทำงานได้ค่อนข้างดีสำหรับข้อผิดพลาด 99% แต่วันนี้ฉันเจอข้อยกเว้นที่เกิดขึ้นระหว่างขั้นตอนการแสดงผล หากคุณServer.Transferหลังจากที่เพจถูกแสดงผลครึ่งหนึ่ง HTML ของเพจที่คุณโอนไปจะเชื่อมต่อกับสิ่งที่แสดงผลไปแล้ว ดังนั้นคุณอาจจบลงด้วยการหักหน้าครึ่งหนึ่งตามด้วยหน้าข้อผิดพลาดด้านล่างนั้น
Kevin

ด้วยเหตุผลบางประการโทรไปที่ Server.Transfer () ทำให้เกิดปัญหาและข้อผิดพลาดไม่ปรากฏขึ้นเลย ดังนั้นฉันไม่แนะนำให้ใช้วิธีนี้ เพียงใช้บรรทัด web.config ตามที่แนะนำข้างต้น (<customErrors mode = "RemoteOnly" defaultRedirect = "~ / error / GeneralError.aspx" redirectMode = "ResponseRewrite" />) และทำงานได้ดี
Naresh Mittal

5

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


1
ในกรณีของฉันฉันต้องการเพียงข้อมูลข้อยกเว้นสำหรับการใช้งานส่วนหลัง แต่นั่นเป็นคำแนะนำที่ดี
nailitdown

2
ไม่ตอบคำถาม
Arne Evertsson

5

นี่คือทางออกของฉัน ..

ใน Global.aspx:

void Application_Error(object sender, EventArgs e)
    {
        // Code that runs when an unhandled error occurs

        //direct user to error page 
        Server.Transfer("~/ErrorPages/Oops.aspx"); 
    }

ใน Oops.aspx:

protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
            LoadError(Server.GetLastError()); 
    }

    protected void LoadError(Exception objError)
    {
        if (objError != null)
        {
            StringBuilder lasterror = new StringBuilder();

            if (objError.Message != null)
            {
                lasterror.AppendLine("Message:");
                lasterror.AppendLine(objError.Message);
                lasterror.AppendLine();
            }

            if (objError.InnerException != null)
            {
                lasterror.AppendLine("InnerException:");
                lasterror.AppendLine(objError.InnerException.ToString());
                lasterror.AppendLine();
            }

            if (objError.Source != null)
            {
                lasterror.AppendLine("Source:");
                lasterror.AppendLine(objError.Source);
                lasterror.AppendLine();
            }

            if (objError.StackTrace != null)
            {
                lasterror.AppendLine("StackTrace:");
                lasterror.AppendLine(objError.StackTrace);
                lasterror.AppendLine();
            }

            ViewState.Add("LastError", lasterror.ToString());
        }
    }

   protected void btnReportError_Click(object sender, EventArgs e)
    {
        SendEmail();
    }

    public void SendEmail()
    {
        try
        {
            MailMessage msg = new MailMessage("webteam", "webteam");
            StringBuilder body = new StringBuilder();

            body.AppendLine("An unexcepted error has occurred.");
            body.AppendLine();

            body.AppendLine(ViewState["LastError"].ToString());

            msg.Subject = "Error";
            msg.Body = body.ToString();
            msg.IsBodyHtml = false;

            SmtpClient smtp = new SmtpClient("exchangeserver");
            smtp.Send(msg);
        }

        catch (Exception ex)
        {
            lblException.Text = ex.Message;
        }
    }

4

การพิจารณาที่สำคัญอย่างหนึ่งที่ฉันคิดว่าทุกคนขาดหายไปที่นี่คือสถานการณ์การจัดสรรภาระงาน (เว็บฟาร์ม) เนื่องจากเซิร์ฟเวอร์ที่เรียกใช้ global.asax อาจแตกต่างจากเซิร์ฟเวอร์ที่เกี่ยวกับการดำเนินการหน้าข้อผิดพลาดที่กำหนดเองการซ่อนวัตถุข้อยกเว้นในแอปพลิเคชันจึงไม่น่าเชื่อถือ

ฉันยังคงมองหาวิธีแก้ปัญหาที่เชื่อถือได้สำหรับปัญหานี้ในการกำหนดค่าเว็บฟาร์มและ / หรือคำอธิบายที่ดีจาก MS ถึงสาเหตุที่คุณไม่สามารถรับข้อยกเว้นกับ Server.GetLastError ในหน้าข้อผิดพลาดที่กำหนดเองได้เช่นเดียวกับที่คุณทำได้ ใน global.asax Application_Error

ปล. การจัดเก็บข้อมูลในคอลเลคชันแอปพลิเคชันไม่ปลอดภัยโดยไม่ต้องล็อกข้อมูลก่อนแล้วจึงปลดล็อก


กรณีนี้จะเกิดขึ้นหากคุณทำการเปลี่ยนเส้นทางฝั่งไคลเอ็นต์เท่านั้น เมื่อทำการโอนย้ายเซิร์ฟเวอร์ทั้งหมดนี้เป็นส่วนหนึ่งของคำขอเดียวดังนั้น application_error -> page_load จะเกิดขึ้นบนเซิร์ฟเวอร์เดียวในฟาร์มตามลำดับ
davewasthere

2

สิ่งนี้เกี่ยวข้องกับ 2 หัวข้อด้านล่างนี้ฉันต้องการรับทั้ง GetHtmlErrorMessage และ Session ในหน้า Error

เซสชันเป็นโมฆะหลังจาก ResponseRewrite

เหตุใด HttpContext.Session จึงเป็นโมฆะเมื่อ redirectMode = ResponseRewrite

ฉันลองและดูวิธีแก้ปัญหาที่ไม่จำเป็น Server.Transfer() or Response.Redirect()

ขั้นแรก: ลบ ResponseRewrite ใน web.config

Web.config

<customErrors defaultRedirect="errorHandler.aspx" mode="On" />

จากนั้น Global.asax

    void Application_Error(object sender, EventArgs e)
    {
         if(Context.IsCustomErrorEnabled)
         {     
            Exception ex = Server.GetLastError();
            Application["TheException"] = ex; //store the error for later
         }
    }

แล้ว errorHandler.aspx.cs

        protected void Page_Load(object sender, EventArgs e)
            {       
                string htmlErrorMessage = string.Empty ;
                Exception ex = (Exception)Application["TheException"];
                string yourSessionValue = HttpContext.Current.Session["YourSessionId"].ToString();

                //continue with ex to get htmlErrorMessage 
                if(ex.GetHtmlErrorMessage() != null){              
                    htmlErrorMessage = ex.GetHtmlErrorMessage();
                }   
                // continue your code
            }

สำหรับการอ้างอิง

http://www.developer.com/net/asp/article.php/3299641/ServerTransfer-Vs-ResponseRedirect.htm


2

มันได้ผลสำหรับฉัน ใน MVC 5


ใน ~\Global.asax

void Application_Error(object sender, EventArgs e)
{
    FTools.LogException();
    Response.Redirect("/Error");
}


ใน~\Controllersสร้างErrorController.cs

using System.Web.Mvc;

namespace MVC_WebApp.Controllers
{
    public class ErrorController : Controller
    {
        // GET: Error
        public ActionResult Index()
        {
            return View("Error");
        }
    }
}


ใน~\Modelsสร้างFunctionTools.cs

using System;
using System.Web;

namespace MVC_WebApp.Models
{
    public static class FTools
    {
        private static string _error;
        private static bool _isError;

        public static string GetLastError
        {
            get
            {
                string cashe = _error;
                HttpContext.Current.Server.ClearError();
                _error = null;
                _isError = false;
                return cashe;
            }
        }
        public static bool ThereIsError => _isError;

        public static void LogException()
        {
            Exception exc = HttpContext.Current.Server.GetLastError();
            if (exc == null) return;
            string errLog = "";
            errLog += "**********" + DateTime.Now + "**********\n";
            if (exc.InnerException != null)
            {
                errLog += "Inner Exception Type: ";
                errLog += exc.InnerException.GetType() + "\n";
                errLog += "Inner Exception: ";
                errLog += exc.InnerException.Message + "\n";
                errLog += "Inner Source: ";
                errLog += exc.InnerException.Source + "\n";
                if (exc.InnerException.StackTrace != null)
                {
                    errLog += "\nInner Stack Trace: " + "\n";
                    errLog += exc.InnerException.StackTrace + "\n";
                }
            }
            errLog += "Exception Type: ";
            errLog += exc.GetType().ToString() + "\n";
            errLog += "Exception: " + exc.Message + "\n";
            errLog += "\nStack Trace: " + "\n";
            if (exc.StackTrace != null)
            {
                errLog += exc.StackTrace + "\n";
            }
            _error = errLog;
            _isError = true;
        }
    }
}


ใน~\Viewsสร้างโฟลเดอร์Error และใน~\Views\Errorสร้างError.cshtml

@using MVC_WebApp.Models
@{
    ViewBag.Title = "Error";
    if (FTools.ThereIsError == false)
    {
        if (Server.GetLastError() != null)
        {
            FTools.LogException();
        }
    }
    if (FTools.ThereIsError == false)
    {
        <br />
        <h1>No Problem!</h1>
    }
    else
    {
        string log = FTools.GetLastError;
        <div>@Html.Raw(log.Replace("\n", "<br />"))</div>
    }
}


หากคุณป้อนที่อยู่นี้ localhost/Error เปิดหน้าโดยไม่มีข้อผิดพลาด



และหากเกิดข้อผิดพลาด เกิดข้อผิดพลาด

แทนการแสดงข้อผิดพลาดตัวแปร 'บันทึก' จะถูกเก็บไว้ในฐานข้อมูล


ที่มา: Microsoft ASP.Net


1

ฉันคิดว่าคุณมีสองทางเลือกที่นี่

คุณสามารถจัดเก็บ Exception สุดท้ายในเซสชันและเรียกดูได้จากหน้าข้อผิดพลาดที่คุณกำหนดเอง หรือคุณสามารถเปลี่ยนเส้นทางไปยังหน้าข้อผิดพลาดที่กำหนดเองของคุณภายในเหตุการณ์ Application_error หากคุณเลือกอย่างหลังคุณต้องแน่ใจว่าคุณใช้เมธอด Server.Transfer

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