หน้าข้อผิดพลาดที่กำหนดเองใน asp.net MVC3


144

ฉันกำลังพัฒนาเว็บไซต์ฐาน MVC3 และฉันกำลังมองหาวิธีแก้ไขข้อผิดพลาดและกำหนดมุมมอง Render แบบกำหนดเองสำหรับข้อผิดพลาดแต่ละประเภท ลองจินตนาการว่าฉันมีตัวควบคุม "ข้อผิดพลาด" ซึ่งการกระทำหลักของเขาคือ "ดัชนี" (หน้าข้อผิดพลาดทั่วไป) และตัวควบคุมนี้จะมีการดำเนินการอีกสองสามครั้งสำหรับข้อผิดพลาดที่อาจปรากฏต่อผู้ใช้เช่น "Handle500" หรือ "HandleActionNotFound"

ดังนั้นทุกข้อผิดพลาดที่อาจเกิดขึ้นบนเว็บไซต์อาจได้รับการจัดการโดยตัวควบคุม "ข้อผิดพลาด" นี้ (ตัวอย่าง: ไม่พบ "ตัวควบคุม" หรือ "การกระทำ", 500, 404, dbException, ฯลฯ )

ฉันใช้ไฟล์ Sitemap เพื่อกำหนดเส้นทางเว็บไซต์ (ไม่ใช่เส้นทาง)

คำถามนี้ตอบแล้วนี่คือคำตอบของ Gweebz

วิธีสุดท้ายของฉันคือดังนี้:

protected void Application_Error() {
//while my project is running in debug mode
if (HttpContext.Current.IsDebuggingEnabled && WebConfigurationManager.AppSettings["EnableCustomErrorPage"].Equals("false"))
{
    Log.Logger.Error("unhandled exception: ", Server.GetLastError());
}
else
{
    try
    {
        var exception = Server.GetLastError();

        Log.Logger.Error("unhandled exception: ", exception);

        Response.Clear();
        Server.ClearError();
        var routeData = new RouteData();
        routeData.Values["controller"] = "Errors";
        routeData.Values["action"] = "General";
        routeData.Values["exception"] = exception;

        IController errorsController = new ErrorsController();
        var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
        errorsController.Execute(rc);
    }
    catch (Exception e)
    {
        //if Error controller failed for same reason, we will display static HTML error page
        Log.Logger.Fatal("failed to display error page, fallback to HTML error: ", e);
        Response.TransmitFile("~/error.html");
    }
}
}

การตั้งค่าควรอยู่ใน web.config เพื่อรองรับสิ่งนี้ เป็นไปได้ว่าคุณจะไม่รวมการตั้งค่า httperrors ใด ๆ ?
philbird

Forums.asp.net/p/1782402/4894514.aspx/ …มีเคล็ดลับดีๆเช่น IE จะไม่แสดงหน้าข้อผิดพลาดของคุณหากมันมีขนาดไม่เกิน 512 ไบต์
RickAndMSFT

คำตอบ:


201

นี่คือตัวอย่างของวิธีที่ฉันจัดการข้อผิดพลาดที่กำหนดเอง ฉันกำหนดErrorsControllerด้วยการกระทำที่จัดการข้อผิดพลาด HTTP ที่แตกต่างกัน:

public class ErrorsController : Controller
{
    public ActionResult General(Exception exception)
    {
        return Content("General failure", "text/plain");
    }

    public ActionResult Http404()
    {
        return Content("Not found", "text/plain");
    }

    public ActionResult Http403()
    {
        return Content("Forbidden", "text/plain");
    }
}

และจากนั้นฉันสมัครเป็นสมาชิกApplication_ErrorในGlobal.asaxและเรียกใช้ตัวควบคุมนี้:

protected void Application_Error()
{
    var exception = Server.GetLastError();
    var httpException = exception as HttpException;
    Response.Clear();
    Server.ClearError();
    var routeData = new RouteData();
    routeData.Values["controller"] = "Errors";
    routeData.Values["action"] = "General";
    routeData.Values["exception"] = exception;
    Response.StatusCode = 500;
    if (httpException != null)
    {
        Response.StatusCode = httpException.GetHttpCode();
        switch (Response.StatusCode)
        {
            case 403:
                routeData.Values["action"] = "Http403";
                break;
            case 404:
                routeData.Values["action"] = "Http404";
                break;
        }
    }

    IController errorsController = new ErrorsController();
    var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
    errorsController.Execute(rc);
}

4
ทราบเพียงเล็กน้อย เนื่องจากฉันต้องการแสดงมุมมองในแต่ละกรณี (404, 500, ฯลฯ ) ในแต่ละ ActionResult ฉันจึงส่งคืนมุมมอง แต่ฉันลองดูเนื้อหา Application_Error และกรณีที่ล้มเหลวฉันจะคืนค่าหน้า HTML แบบคงที่ (ฉันสามารถโพสต์รหัสได้ถ้ามีคนต้องการ)
John Louros

4
ฉันไม่สามารถรับมุมมองมีดโกนเพื่อแสดงผลโดยใช้โซลูชันนี้ใน MVC3 return View (model) เช่นรับหน้าจอว่างเปล่าเท่านั้น
Extrakun

2
เพิ่ม TrySkipIisCustomErrors เพื่อแก้ไขสำหรับการรวม IIS7 ดูstackoverflow.com/questions/1706934/…
Pavel Savara

1
@ajbeaven Executeเป็นวิธีการที่กำหนดไว้ในIControllerส่วนต่อประสาน สิ่งนี้ไม่สามารถป้องกันได้ ดูโค้ดของฉันให้ละเอียดยิ่งขึ้น: IController errorsController = new ErrorsController();และสังเกตชนิดของerrorsControllerตัวแปรที่ฉันเรียกใช้Executeเมธอด เป็นประเภทIControllerดังนั้นจึงไม่มีอะไรขัดขวางคุณจากการเรียกใช้วิธีนี้ และโดยวิธีการExecuteได้รับการคุ้มครองในระดับควบคุมเช่นกันใน MVC 3 ดังนั้นจึงไม่มีการเปลี่ยนแปลงในเรื่องนี้
ดารินทร์ดิมิทรอฟ

2
แก้ไขโดยระบุประเภทเนื้อหาของการตอบสนองอย่างชัดเจน:Response.ContentType = "text/html";
ajbeaven

18

นี่คือบทความเพิ่มเติมวิธีการสร้างหน้าข้อผิดพลาดที่กำหนดเองกับ MVC http://kitsula.com/Article/MVC-Custom-Error-Pages


6

คุณสามารถทำได้ในไฟล์ Web.Config นี่คือตัวอย่างที่ใช้งานได้ใน IIS 7.5

     <system.webServer>
          <httpErrors errorMode="DetailedLocalOnly" defaultResponseMode="File">
                <remove statusCode="502" subStatusCode="-1" />
                <remove statusCode="501" subStatusCode="-1" />
                <remove statusCode="412" subStatusCode="-1" />
                <remove statusCode="406" subStatusCode="-1" />
                <remove statusCode="405" subStatusCode="-1" />
                <remove statusCode="404" subStatusCode="-1" />
                <remove statusCode="403" subStatusCode="-1" />
                <remove statusCode="401" subStatusCode="-1" />
                <remove statusCode="500" subStatusCode="-1" />
                <error statusCode="500" path="/notfound.html" responseMode="ExecuteURL" />
                <error statusCode="401" prefixLanguageFilePath="" path="/500.html" responseMode="ExecuteURL" />
                <error statusCode="403" prefixLanguageFilePath="" path="/403.html" responseMode="ExecuteURL" />
                <error statusCode="404" prefixLanguageFilePath="" path="/404.html" responseMode="ExecuteURL" />
                <error statusCode="405" prefixLanguageFilePath="" path="/405.html" responseMode="ExecuteURL" />
                <error statusCode="406" prefixLanguageFilePath="" path="/406.html" responseMode="ExecuteURL" />
                <error statusCode="412" prefixLanguageFilePath="" path="/412.html" responseMode="ExecuteURL" />
                <error statusCode="501" prefixLanguageFilePath="" path="/501.html" responseMode="ExecuteURL" />
                <error statusCode="502" prefixLanguageFilePath="" path="/genericerror.html" responseMode="ExecuteURL" />
           </httpErrors>
</system.webServer>

3

ฉันเห็นว่าคุณเพิ่มค่าการกำหนดค่าEnableCustomErrorPageแล้วและคุณกำลังตรวจสอบIsDebuggingEnabledเพื่อพิจารณาว่าจะจัดการข้อผิดพลาดของคุณหรือไม่

เนื่องจากมีการ<customErrors/>กำหนดค่าใน ASP.NET อยู่แล้ว(ซึ่งมีความหมายตรงตามวัตถุประสงค์นี้) จึงเป็นเรื่องที่ง่ายที่สุดที่จะพูดว่า:

    protected void Application_Error()
    {
        if (HttpContext.Current == null) 
        {
                // errors in Application_Start will end up here                
        }
        else if (HttpContext.Current.IsCustomErrorEnabled)
        {
                // custom exception handling
        }
    }

จากนั้นในการกำหนดค่าที่คุณใส่<customErrors mode="RemoteOnly" />ซึ่งมีความปลอดภัยในการปรับใช้เช่นนั้นและเมื่อคุณจำเป็นต้องทดสอบหน้าข้อผิดพลาดที่กำหนดเองของคุณคุณจะต้องตั้งค่าเป็นเพื่อ<customErrors mode="On" />ให้คุณสามารถตรวจสอบว่ามันทำงาน

โปรดทราบว่าคุณต้องตรวจสอบว่าHttpContext.Currentเป็นโมฆะหรือไม่เนื่องจากข้อยกเว้นApplication_Startจะยังคงเป็นวิธีนี้แม้ว่าจะไม่มีบริบทที่ใช้งานอยู่


2

คุณสามารถแสดงหน้าข้อผิดพลาดที่เป็นมิตรกับผู้ใช้ด้วยรหัสสถานะ http ที่ถูกต้องโดยใช้โมดูลจัดการข้อยกเว้นที่เป็นมิตรกับผู้ใช้ของ Jeff Atwood โดยมีการแก้ไขเล็กน้อยสำหรับรหัสสถานะ http มันทำงานโดยไม่มีการเปลี่ยนเส้นทางใด ๆ แม้ว่ารหัสจะมาจากปี 2004 (!) แต่ก็ใช้ได้ดีกับ MVC สามารถกำหนดค่าทั้งหมดใน web.config ของคุณโดยไม่มีการเปลี่ยนแปลงซอร์สโค้ดโครงการ MVC เลย

การปรับเปลี่ยนที่จำเป็นเพื่อส่งคืนสถานะ HTTP ดั้งเดิมแทนที่จะ200อธิบายสถานะในโพสต์ฟอรัมที่เกี่ยวข้องนี้

โดยทั่วไปใน Handler.vb คุณสามารถเพิ่มสิ่งที่ชอบ:

' In the header...
Private _exHttpEx As HttpException = Nothing

' At the top of Public Sub HandleException(ByVal ex As Exception)...
HttpContext.Current.Response.StatusCode = 500
If TypeOf ex Is HttpException Then
    _exHttpEx = CType(ex, HttpException)
    HttpContext.Current.Response.StatusCode = _exHttpEx.GetHttpCode()
End If

0

ฉันใช้ MVC 4.5 และฉันมีปัญหากับโซลูชันของดาริน หมายเหตุ: โซลูชันของดารินนั้นยอดเยี่ยมและฉันใช้มันเพื่อหาคำตอบ นี่คือวิธีแก้ไขของฉัน:

protected void Application_Error(object sender, EventArgs e)
{           
var exception = Server.GetLastError();
var httpException = exception as HttpException;
Response.StatusCode = httpException.GetHttpCode();

Response.Clear();
Server.ClearError();


if (httpException != null)
{
    var httpContext = HttpContext.Current;

    httpContext.RewritePath("/Errors/InternalError", false);

    // MVC 3 running on IIS 7+
    if (HttpRuntime.UsingIntegratedPipeline)
    {
        switch (Response.StatusCode)
        {
            case 403:
                httpContext.Server.TransferRequest("/Errors/Http403", true);
                break;
            case 404:
                httpContext.Server.TransferRequest("/Errors/Http404", true);
                break;
            default:
                httpContext.Server.TransferRequest("/Errors/InternalError", true);
                break;
        }
    }
    else
    {
        switch (Response.StatusCode)
        {
            case 403:
                httpContext.RewritePath(string.Format("/Errors/Http403", true));
                break;
            case 404:
                httpContext.RewritePath(string.Format("/Errors/Http404", true));
                break;
            default:
                httpContext.RewritePath(string.Format("/Errors/InternalError", true));
                break;
        }

        IHttpHandler httpHandler = new MvcHttpHandler();
        httpHandler.ProcessRequest(httpContext);
    }
}
}

2
คุณมีปัญหาอะไรกับวิธีการแก้ปัญหาของดาริน?
Kenny Evitt

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