ฉันจะจัดการ 404 ใน ASP.NET MVC ได้อย่างถูกต้องได้อย่างไร?


432

ฉันใช้ RC2

ใช้การกำหนดเส้นทาง URL:

routes.MapRoute(
    "Error",
     "{*url}",
     new { controller = "Errors", action = "NotFound" }  // 404s
);

ด้านบนดูเหมือนว่าจะดูแลคำขอเช่นนี้ (สมมติว่าการตั้งค่าตารางเส้นทางเริ่มต้นโดยโครงการ MVC เริ่มต้น): "/ blah / blah / blah / blah / blah"

การเอาชนะ HandleUnknownAction () ในตัวควบคุม:

// 404s - handle here (bad action requested
protected override void HandleUnknownAction(string actionName) {
    ViewData["actionName"] = actionName;
    View("NotFound").ExecuteResult(this.ControllerContext);
}  

อย่างไรก็ตามกลยุทธ์ก่อนหน้านี้ไม่จัดการการร้องขอไปยังตัวควบคุมที่ไม่รู้จัก / ไม่รู้จัก ตัวอย่างเช่นฉันไม่มี "/ IDoNotExist" ถ้าฉันขอสิ่งนี้ฉันจะได้รับหน้าเว็บ 404 ทั่วไปจากเว็บเซิร์ฟเวอร์และไม่ใช่ 404 ของฉันหากฉันใช้ routing + override

ในที่สุดคำถามของฉันคือ: มีวิธีใดที่จะรับคำขอประเภทนี้โดยใช้เส้นทางหรืออย่างอื่นในกรอบ MVC เอง

หรือฉันควรเริ่มต้นใช้ Web.Config customErrors เป็นตัวจัดการ 404 ของฉันและลืมสิ่งนี้ทั้งหมดหรือไม่ ฉันถือว่าถ้าฉันไปกับ customErrors ฉันจะต้องเก็บหน้า 404 ทั่วไปนอก / Views เนื่องจากข้อ จำกัด ของ Web.Config ในการเข้าถึงโดยตรง


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

คุณสามารถดูวิธีแก้ปัญหานี้ได้เช่นกันblog.dantup.com/2009/04/…
พัฒนา

ben.onfabrik.com/posts/aspnet-mvc-custom-error-pagesนอกจากนี้ยังมีข้อมูลที่ดี
คริส S

4
เป็นที่น่าเสียดายที่มีการเปิดตัว 4 เสถียรในภายหลังและนานกว่า 5 ปีสถานการณ์การจัดการ 404s ใน asp.net MVC + IIS ยังไม่ได้รับการปรับปรุงให้ดีขึ้นจริง ๆ และนี่ยังคงเป็นคำถามและคำตอบเกี่ยวกับวิธีการจัดการ
joelmdev

คำตอบ:


271

รหัสถูกนำมาจากhttp://blogs.microsoft.co.il/blogs/shay/archive/2009/03/06/real-world-error-hadnling-in-asp-net-mvc-rc2.aspxและทำงาน ใน ASP.net MVC 1.0 เช่นกัน

นี่คือวิธีที่ฉันจัดการข้อยกเว้น http:

protected void Application_Error(object sender, EventArgs e)
{
   Exception exception = Server.GetLastError();
   // Log the exception.

   ILogger logger = Container.Resolve<ILogger>();
   logger.Error(exception);

   Response.Clear();

   HttpException httpException = exception as HttpException;

   RouteData routeData = new RouteData();
   routeData.Values.Add("controller", "Error");

   if (httpException == null)
   {
       routeData.Values.Add("action", "Index");
   }
   else //It's an Http Exception, Let's handle it.
   {
       switch (httpException.GetHttpCode())
       {
          case 404:
              // Page not found.
              routeData.Values.Add("action", "HttpError404");
              break;
          case 500:
              // Server error.
              routeData.Values.Add("action", "HttpError500");
              break;

           // Here you can handle Views to other error codes.
           // I choose a General error template  
           default:
              routeData.Values.Add("action", "General");
              break;
      }
  }           

  // Pass exception details to the target error View.
  routeData.Values.Add("error", exception);

  // Clear the error on server.
  Server.ClearError();

  // Avoid IIS7 getting in the middle
  Response.TrySkipIisCustomErrors = true; 

  // Call target Controller and pass the routeData.
  IController errorController = new ErrorController();
  errorController.Execute(new RequestContext(    
       new HttpContextWrapper(Context), routeData));
}

23
อัปเดต: การตรวจสอบ http 404 เป็นสิ่งจำเป็นอย่างแน่นอน แต่ฉันยังไม่แน่ใจเมื่อคุณได้รับ 500 นอกจากนี้คุณยังต้องตั้ง Response.StatusCode = 404 หรือ 500 ให้ชัดเจนมิฉะนั้น google จะเริ่มสร้างดัชนีหน้าเว็บเหล่านี้ หากคุณส่งคืนรหัสสถานะ 200 ซึ่งรหัสนี้ใช้ในขณะนี้
Simon_Weaver

6
@Simon_Weaver: เห็นด้วย! จำเป็นต้องส่งคืนรหัสสถานะ 404 มันใช้งานไม่ได้เป็น 404 จนกว่ามันจะแก้ปัญหา ตรวจสอบสิ่งนี้: codinghorror.com/blog/2007/03/…
Matt Kocaj

1
มีข้อผิดพลาดพื้นฐานที่มีข้อเสนอแนะทั้งหมดนี้ - เมื่อถึงเวลาดำเนินการตามที่ได้รับการขึ้นสู่ Global.asax HttpContext มากเกินไปจะหายไป คุณไม่สามารถกำหนดเส้นทางกลับไปยังตัวควบคุมของคุณได้ตามตัวอย่างที่แนะนำ อ้างถึงความคิดเห็นในลิงค์บล็อกที่ด้านบน
Matt Kocaj

3
เนื่องจากความคิดเห็นบางส่วนจากด้านบนและโพสต์ที่กล่าวถึงการเชื่อมโยงจึงไม่ปรากฏขึ้น errorcontroller ถูก hit แต่หน้าจอว่างเปล่ากลับมา (ใช้ mvc 3)
RyanW

4
บางสิ่งบางอย่างไม่ถูกต้องจุดประสงค์ทั้งหมดของ MVC คือการลบสิ่งที่เป็นนามธรรมและยังอยู่ที่นี่อีกครั้ง ...
Alex Nolasco

255

ข้อกำหนดสำหรับ 404

ต่อไปนี้เป็นข้อกำหนดของฉันสำหรับโซลูชัน 404 และด้านล่างฉันแสดงวิธีการนำไปใช้:

  • ฉันต้องการจัดการเส้นทางที่ตรงกันด้วยการกระทำที่ไม่ดี
  • ฉันต้องการจัดการเส้นทางที่จับคู่กับคอนโทรลเลอร์ที่ไม่ดี
  • ฉันต้องการจัดการเส้นทางที่ไม่ตรงกัน (URL ที่แอพของฉันไม่เข้าใจ) - ฉันไม่ต้องการให้เดือดดาลกับ Global.asax หรือ IIS เพราะฉันไม่สามารถเปลี่ยนเส้นทางกลับไปที่แอป MVC ของฉันได้อย่างถูกต้อง
  • ฉันต้องการวิธีจัดการในลักษณะเดียวกับข้างต้น 404s ที่กำหนดเอง - เช่นเมื่อมีการส่ง ID สำหรับวัตถุที่ไม่มีอยู่ (อาจถูกลบ)
  • ฉันต้องการ 404 ทั้งหมดของฉันเพื่อส่งคืนมุมมอง MVC (ไม่ใช่หน้าคงที่) ซึ่งฉันสามารถปั๊มข้อมูลได้มากขึ้นในภายหลังหากจำเป็น ( การออกแบบ 404 ที่ดี ) และพวกเขาจะต้องส่งคืนรหัสสถานะ HTTP 404

สารละลาย

ฉันคิดว่าคุณควรบันทึกApplication_Errorใน Global.asax สำหรับสิ่งที่สูงกว่าเช่นข้อยกเว้นที่ไม่สามารถจัดการได้และการบันทึก (เช่นคำตอบของ Shay Jacobyแสดงให้เห็น) แต่ไม่ใช่การจัดการ 404 นี่คือเหตุผลที่คำแนะนำของฉันเก็บไฟล์ 404 จากไฟล์ Global.asax

ขั้นตอนที่ 1: มีสถานที่ทั่วไปสำหรับตรรกะข้อผิดพลาด 404

นี่เป็นความคิดที่ดีสำหรับการบำรุงรักษา ใช้ErrorControllerเพื่อให้การปรับปรุงในอนาคตในหน้า 404 ที่ออกแบบมาอย่างดีของคุณสามารถปรับเปลี่ยนได้อย่างง่ายดาย นอกจากนี้ยังให้แน่ใจว่าการตอบสนองของคุณมีรหัส 404 !

public class ErrorController : MyController
{
    #region Http404

    public ActionResult Http404(string url)
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        var model = new NotFoundViewModel();
        // If the url is relative ('NotFound' route) then replace with Requested path
        model.RequestedUrl = Request.Url.OriginalString.Contains(url) & Request.Url.OriginalString != url ?
            Request.Url.OriginalString : url;
        // Dont get the user stuck in a 'retry loop' by
        // allowing the Referrer to be the same as the Request
        model.ReferrerUrl = Request.UrlReferrer != null &&
            Request.UrlReferrer.OriginalString != model.RequestedUrl ?
            Request.UrlReferrer.OriginalString : null;

        // TODO: insert ILogger here

        return View("NotFound", model);
    }
    public class NotFoundViewModel
    {
        public string RequestedUrl { get; set; }
        public string ReferrerUrl { get; set; }
    }

    #endregion
}

ขั้นตอนที่ 2: ใช้คลาสตัวควบคุมพื้นฐานเพื่อให้คุณสามารถเรียกใช้แอคชั่น 404 ที่กำหนดเองและวางสายได้อย่างง่ายดาย HandleUnknownAction

404s ใน ASP.NET MVC จำเป็นต้องติดตั้งไว้ตามที่ต่างๆ HandleUnknownActionที่แรกก็คือ

InvokeHttp404วิธีการสร้างสถานที่ที่พบสำหรับเส้นทางใหม่ไปยังErrorControllerและใหม่ของเราHttp404ดำเนินการ คิดว่าแห้ง !

public abstract class MyController : Controller
{
    #region Http404 handling

    protected override void HandleUnknownAction(string actionName)
    {
        // If controller is ErrorController dont 'nest' exceptions
        if (this.GetType() != typeof(ErrorController))
            this.InvokeHttp404(HttpContext);
    }

    public ActionResult InvokeHttp404(HttpContextBase httpContext)
    {
        IController errorController = ObjectFactory.GetInstance<ErrorController>();
        var errorRoute = new RouteData();
        errorRoute.Values.Add("controller", "Error");
        errorRoute.Values.Add("action", "Http404");
        errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
        errorController.Execute(new RequestContext(
             httpContext, errorRoute));

        return new EmptyResult();
    }

    #endregion
}

ขั้นตอนที่ 3: ใช้ Dependency Injection ใน Controller Factory ของคุณและเชื่อมต่อ HttpExceptions 404 HttpExceptions

ชอบ (ไม่จำเป็นต้องเป็น StructureMap):

ตัวอย่าง MVC1.0:

public class StructureMapControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(controllerType);
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                IController errorController = ObjectFactory.GetInstance<ErrorController>();
                ((ErrorController)errorController).InvokeHttp404(RequestContext.HttpContext);

                return errorController;
            }
            else
                throw ex;
        }

        return ObjectFactory.GetInstance(controllerType) as Controller;
    }
}

ตัวอย่าง MVC2.0:

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(requestContext, controllerType);
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == 404)
            {
                IController errorController = ObjectFactory.GetInstance<ErrorController>();
                ((ErrorController)errorController).InvokeHttp404(requestContext.HttpContext);

                return errorController;
            }
            else
                throw ex;
        }

        return ObjectFactory.GetInstance(controllerType) as Controller;
    }

ฉันคิดว่าเป็นการดีกว่าที่จะตรวจจับข้อผิดพลาดที่ใกล้เคียงกับจุดกำเนิด นี่คือเหตุผลที่ฉันชอบด้านบนกับApplication_Errorตัวจัดการ

นี่คือสถานที่ที่สองในการจับ 404s

ขั้นตอนที่ 4: เพิ่มเส้นทาง NotFound ไปที่ Global.asax สำหรับ URL ที่ไม่สามารถแยกวิเคราะห์ลงในแอปของคุณ

เส้นทางนี้ควรชี้ไปที่Http404การกระทำของเรา โปรดสังเกตว่าurlพารามิเตอร์จะเป็น URL สัมพัทธ์เนื่องจากเอ็นจิ้นการเราติ้งกำลังแยกส่วนโดเมนที่นี่ นั่นคือเหตุผลที่เรามีตรรกะ URL ที่มีเงื่อนไขทั้งหมดในขั้นตอนที่ 1

        routes.MapRoute("NotFound", "{*url}", 
            new { controller = "Error", action = "Http404" });

นี่เป็นสถานที่ที่สามและสุดท้ายในการจับ 404s ในแอป MVC ที่คุณไม่ได้เรียกตัวเอง หากคุณไม่พบเส้นทางที่ไม่มีใครเทียบได้ที่นี่ MVC จะผ่านปัญหาจนถึง ASP.NET (Global.asax) และคุณไม่ต้องการสิ่งนั้นในสถานการณ์นี้

ขั้นตอนที่ 5: สุดท้ายเรียก 404s เมื่อแอปของคุณไม่พบบางสิ่ง

เช่นเมื่อมีการส่ง ID ที่ไม่ถูกต้องไปยังตัวควบคุมสินเชื่อของฉัน (มาจากMyController):

    //
    // GET: /Detail/ID

    public ActionResult Detail(int ID)
    {
        Loan loan = this._svc.GetLoans().WithID(ID);
        if (loan == null)
            return this.InvokeHttp404(HttpContext);
        else
            return View(loan);
    }

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

ขอบคุณสำหรับคำติชม ฉันชอบที่จะได้รับมากขึ้น

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


12
ขอบคุณสำหรับบทความเต็มรูปแบบ อีกหนึ่งคือเมื่อทำงานภายใต้ IIS7 คุณต้องเพิ่มการตั้งค่าคุณสมบัติ "TrySkipIisCustomErrors" เป็นจริง มิฉะนั้น IIS จะยังคงส่งคืนหน้าเริ่มต้น 404 เราได้เพิ่ม Response.TrySkipIiisCustomErrors = true; หลังบรรทัดในขั้นตอนที่ 5 ที่ตั้งรหัสสถานะ msdn.microsoft.com/en-us/library/ ......
Rick

1
@Ryan customErrorsส่วนของ web.config กำหนดหน้าเปลี่ยนเส้นทางแบบคงที่ที่จัดการในระดับสูงใน aspnet ถ้าไม่ใช่ IIS นี่ไม่ใช่สิ่งที่ฉันต้องการตามที่ฉันต้องการเพื่อให้ได้ผลลัพธ์เป็นมุมมอง MVC (ดังนั้นฉันจึงสามารถมีข้อมูลในพวกเขา ฯลฯ ) ฉันจะไม่พูดอย่างเด็ดขาดว่า " customErrorsล้าสมัยใน MVC" แต่สำหรับฉันและโซลูชัน 404 นี้พวกเขาแน่นอน
Matt Kocaj

1
นอกจากนี้บางคนสามารถอัปเดตขั้นตอนที่ 3 เพื่อไม่ให้ StructureMap ใช้หรือไม่ อาจเป็นเพียงแค่ ControllerFactory ทั่วไปที่จะใช้งานได้ง่ายถ้าคุณยังไม่ได้ใช้ ControllerFactory
David Murdoch

7
ใช้งานได้ดีสำหรับ MVC3 ฉันเปลี่ยนObjectFactory.GetInstanceมาใช้ MVC3 DependencyResolver.Current.GetServiceแทนดังนั้นจึงเป็นเรื่องทั่วไปมากกว่า ฉันใช้ Ninject
kamranicus

122
ใครบ้างคิดว่ามันบ้าอย่างอดทนว่าสิ่งที่พบได้ทั่วไปเช่น 404 ในเว็บเฟรมเวิร์กมีความซับซ้อนมาก
quentin-starin

235

ASP.NET MVC ไม่รองรับ 404 หน้าแบบกำหนดเองได้เป็นอย่างดี โรงงานคอนโทรลเลอร์ที่กำหนดเองเส้นทาง catch-all, คลาสคอนโทรลเลอร์พื้นฐานพร้อมHandleUnknownAction- argh!

หน้าข้อผิดพลาดที่กำหนดเองของ IIS เป็นทางเลือกที่ดีกว่า:

web.config

<system.webServer>
  <httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="404" />
    <error statusCode="404" responseMode="ExecuteURL" path="/Error/PageNotFound" />
  </httpErrors>
</system.webServer>

ErrorController

public class ErrorController : Controller
{
    public ActionResult PageNotFound()
    {
        Response.StatusCode = 404;
        return View();
    }
}

ตัวอย่างโครงการ


38
สิ่งนี้ควรเป็นคำตอบที่ยอมรับ !!! ทำงานได้ดีบน ASP.NET MVC 3 กับ IIS Express
Andrei Rînea

7
หากคุณใช้ IIS7 + นี่เป็นวิธีที่แน่นอน +1!
elo80ka

3
เป็นไปได้หรือไม่ที่จะแสดงสถานะ 404 เมื่อคุณทำงานใน JSON ภายในโครงการเดียวกัน
VinnyG

6
นี่ใช้งานได้ดีใน iis express แต่ทันทีที่ฉันปรับใช้ไซต์ในการผลิต IIS 7.5 ทั้งหมดที่ฉันได้รับคือหน้าขาวแทนที่จะเป็นมุมมองข้อผิดพลาด
Moulde

2
จากการทดสอบของฉัน (ด้วย MVC3) การแบ่งนี้customErrors mode="On"พร้อมกับการHandleErrorAttributeใช้งานได้ หน้าข้อผิดพลาดที่กำหนดเองสำหรับข้อยกเว้นที่ไม่สามารถจัดการได้ในการกระทำของคอนโทรลเลอร์จะไม่ถูกแสดงอีกต่อไป
Slauma

153

คำตอบด่วน / TL; DR

ป้อนคำอธิบายรูปภาพที่นี่

สำหรับคนขี้เกียจที่นั่น:

Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0

จากนั้นลบบรรทัดนี้ออก global.asax

GlobalFilters.Filters.Add(new HandleErrorAttribute());

และนี่สำหรับ IIS7 + และ IIS Express เท่านั้น

หากคุณกำลังใช้ Cassini .. ดี .. เอ่อ .. เอ่อ .. อึดอัด ... อึดอัด


ยาวอธิบายคำตอบ

ฉันรู้ว่าสิ่งนี้ได้รับคำตอบแล้ว แต่คำตอบนั้นง่ายจริงๆ (ให้กำลังใจกับDavid FowlerและDamian Edwardsสำหรับการตอบคำถามนี้จริงๆ)

มีความต้องการที่จะทำอะไรที่กำหนดเองไม่มี

สำหรับASP.NET MVC3บิตและชิ้นส่วนทั้งหมดอยู่ที่นั่น

ขั้นตอนที่ 1 -> อัปเดต web.config ของคุณในสองจุด

<system.web>
    <customErrors mode="On" defaultRedirect="/ServerError">
      <error statusCode="404" redirect="/NotFound" />
    </customErrors>

และ

<system.webServer>
    <httpErrors errorMode="Custom">
      <remove statusCode="404" subStatusCode="-1" />
      <error statusCode="404" path="/NotFound" responseMode="ExecuteURL" />
      <remove statusCode="500" subStatusCode="-1" />
      <error statusCode="500" path="/ServerError" responseMode="ExecuteURL" />
    </httpErrors>    

...
<system.webServer>
...
</system.web>

ตอนนี้จดบันทึก ROUTES ที่ฉันตัดสินใจใช้อย่างระมัดระวัง คุณสามารถใช้อะไรก็ได้ แต่เส้นทางของฉันคือ

  • /NotFound <- สำหรับ 404 ไม่พบหน้าข้อผิดพลาด
  • /ServerError<- สำหรับข้อผิดพลาดอื่น ๆ รวมถึงข้อผิดพลาดที่เกิดขึ้นในรหัสของฉัน นี่เป็นข้อผิดพลาดเซิร์ฟเวอร์ภายใน 500 ข้อ

ดูว่าส่วนแรก<system.web>มีเพียงรายการเดียวที่กำหนดเองได้อย่างไร statusCode="404"รายการ? ฉันระบุรหัสสถานะเพียงรหัสเดียวเนื่องจากข้อผิดพลาดอื่น ๆ ทั้งหมดรวมถึง500 Server Error(เช่นข้อผิดพลาดที่น่ารำคาญที่เกิดขึ้นเมื่อรหัสของคุณมีข้อผิดพลาดและขัดข้องคำขอของผู้ใช้) .. ข้อผิดพลาดอื่น ๆ ทั้งหมดได้รับการจัดการโดยการตั้งค่าdefaultRedirect="/ServerError".. หากคุณไม่พบหน้า 404 กรุณาข้ามเส้นทาง/ServerErrorไป

ตกลง. มันออกนอกเส้นทางไปยังเส้นทางของฉันที่ระบุไว้global.asax

ขั้นตอนที่ 2 - การสร้างเส้นทางใน Global.asax

นี่คือส่วนเส้นทางแบบเต็มของฉัน ..

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.IgnoreRoute("{*favicon}", new {favicon = @"(.*/)?favicon.ico(/.*)?"});

    routes.MapRoute(
        "Error - 404",
        "NotFound",
        new { controller = "Error", action = "NotFound" }
        );

    routes.MapRoute(
        "Error - 500",
        "ServerError",
        new { controller = "Error", action = "ServerError"}
        );

    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new {controller = "Home", action = "Index", id = UrlParameter.Optional}
        );
}

นั่นแสดงเส้นทางที่ไม่สนใจสองเส้นทาง -> axd'sและfavicons(ooo! bonus เพิกเฉยเส้นทางสำหรับคุณ!) จากนั้น (และคำสั่งนั้นใช้งานได้ที่นี่) ฉันมีเส้นทางการจัดการข้อผิดพลาดสองเส้นทางที่ชัดเจน .. ตามด้วยเส้นทางอื่น ๆ ในกรณีนี้เป็นค่าเริ่มต้น แน่นอนว่าฉันมีมากกว่านี้ แต่พิเศษสำหรับเว็บไซต์ของฉัน เพียงตรวจสอบให้แน่ใจว่าเส้นทางข้อผิดพลาดอยู่ด้านบนของรายการ สั่งซื้อสินค้าที่มีความจำเป็น

สุดท้ายในขณะที่เราอยู่ในglobal.asaxไฟล์ของเราเราไม่ได้ลงทะเบียนคุณลักษณะ HandleError ทั่วโลก ไม่ไม่ไม่ครับ Nadda Nope Nien เชิงลบ noooooooooo ...

ลบบรรทัดนี้ออก global.asax

GlobalFilters.Filters.Add(new HandleErrorAttribute());

ขั้นตอนที่ 3 - สร้างตัวควบคุมด้วยวิธีการดำเนินการ

ตอนนี้ .. เราเพิ่มตัวควบคุมด้วยวิธีการสองวิธี ...

public class ErrorController : Controller
{
    public ActionResult NotFound()
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

    public ActionResult ServerError()
    {
        Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        // Todo: Pass the exception into the view model, which you can make.
        //       That's an exercise, dear reader, for -you-.
        //       In case u want to pass it to the view, if you're admin, etc.
        // if (User.IsAdmin) // <-- I just made that up :) U get the idea...
        // {
        //     var exception = Server.GetLastError();
        //     // etc..
        // }

        return View();
    }

    // Shhh .. secret test method .. ooOOooOooOOOooohhhhhhhh
    public ActionResult ThrowError()
    {
        throw new NotImplementedException("Pew ^ Pew");
    }
}

ตกลงให้ตรวจสอบนี้ ก่อนอื่นไม่มีแอ [HandleError]ททริบิวต์ที่นี่ ทำไม? เนื่องจากASP.NETเฟรมเวิร์กในตัวจัดการข้อผิดพลาดอยู่แล้วและเราได้ระบุอึทั้งหมดที่เราต้องทำเพื่อจัดการข้อผิดพลาด :) มันอยู่ในวิธีนี้!

ต่อไปฉันมีสองวิธีในการดำเนินการ ไม่มีอะไรยาก หากคุณต้องการแสดงข้อมูลข้อยกเว้นคุณสามารถใช้Server.GetLastError()เพื่อรับข้อมูลนั้นได้

โบนัส WTF: ใช่ฉันได้ทำวิธีที่สามเพื่อทดสอบการจัดการข้อผิดพลาด

ขั้นตอนที่ 4 - สร้างมุมมอง

และสุดท้ายสร้างสองมุมมอง วาง em ในมุมมองปกติสำหรับคอนโทรลเลอร์นี้

ป้อนคำอธิบายรูปภาพที่นี่

ความคิดเห็นโบนัส

  • คุณไม่ต้องการ Application_Error(object sender, EventArgs e)
  • ดังกล่าวข้างต้นขั้นตอนการทำงานทั้งหมด 100% อย่างสมบูรณ์แบบด้วยELMAH Elmah เต็มไปด้วย wroxs!

และที่เพื่อนของฉันควรจะเป็น

ตอนนี้ขอแสดงความยินดีกับการอ่านมากและมีรางวัลยูนิคอร์น!

ป้อนคำอธิบายรูปภาพที่นี่


ดังนั้นฉันจึงพยายามที่จะใช้สิ่งนี้ แต่มีปัญหาสองอย่าง ... ก่อนอื่นคุณต้องมี ~ ก่อนที่พา ธ ใน weeb.config มิเช่นนั้นจะใช้ไม่ได้กับไดเรกทอรีเสมือน 2- ถ้าข้อผิดพลาดที่กำหนดเองของ IIS เริ่มทำงานและมุมมองใช้เลย์เอาต์ก็ไม่ได้แสดงผลเลยเพียงแค่หน้าขาว ฉันแก้ไขได้โดยเพิ่มบรรทัดนี้ลงในตัวควบคุม "Response.TrySkipIisCustomErrors = true;" . อย่างไรก็ตามมันยังใช้งานไม่ได้หากคุณไปที่ URL ที่เป็นไฟล์ แต่ 404 .. เช่นmysite / อะไรก็ตาม / fake.html ได้รับหน้าขาว
Robert Noack

3
-1 ขออภัยสำหรับฉันทางออกใด ๆ ที่เปลี่ยน url สำหรับ 404 นั้นผิด และด้วย webconfig ไม่มีทางใน MVC ที่คุณสามารถจัดการได้โดยไม่ต้องเปลี่ยน url หรือคุณจำเป็นต้องสร้างไฟล์ html แบบคงที่หรือ aspx (ใช่, ไฟล์ aspx แบบเก่า) เพื่อให้สามารถทำได้ ทางออกของคุณดีถ้าคุณชอบที่?aspxerrorpath=/er/not/foundจะมี URL
Gutek

7
นี้อาจฟังดูแปลกจริงๆ - แต่คำตอบของฉันถูกจัดให้ทุกเพศทุกวัยที่ผ่านมาและผมเห็นด้วยกับ @Gutek ของคุณผมไม่ชอบทำเปลี่ยนเส้นทางไปยังหน้าข้อผิดพลาดอีกต่อไป ฉันเคยใช้ (อ้างอิงคำตอบของฉัน: P) หากข้อผิดพลาดเกิดขึ้นใน / some / resource .. จากนั้นทรัพยากรนั้นควรส่งคืน 404 หรือ 500 เป็นต้นการทำ SEO ขนาดใหญ่มีความหมายเป็นอย่างอื่น อ่า .. เวลาเปลี่ยนไปอย่างไร :)
Pure.Krome

@Gutek คุณรู้หรือไม่ว่า customErrors redirectMode = "ResponseRewrite"? และการคืน 404 นั้นไม่เหมาะจากมุมมองด้านความปลอดภัย
Jowen

1
@Chris <แทรกเทพที่คุณชื่นชอบที่นี่> ประณามมัน ฉันจำไม่ได้ว่าตอนนี้เป็นอะไร คงมีการสะสม meme ของฉันเพื่อช่วยเหลือ ... และ .. ได้รับการแก้ไข
Pure.Krome

86

ฉันได้ตรวจสอบจำนวนมากเกี่ยวกับวิธีจัดการ 404 ใน MVC (เฉพาะ MVC3)อย่างถูกต้องและสิ่งนี้ IMHO เป็นทางออกที่ดีที่สุดที่ฉันได้รับ:

ใน global.asax:

public class MvcApplication : HttpApplication
{
    protected void Application_EndRequest()
    {
        if (Context.Response.StatusCode == 404)
        {
            Response.Clear();

            var rd = new RouteData();
            rd.DataTokens["area"] = "AreaName"; // In case controller is in another area
            rd.Values["controller"] = "Errors";
            rd.Values["action"] = "NotFound";

            IController c = new ErrorsController();
            c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));
        }
    }
}

ErrorsController:

public sealed class ErrorsController : Controller
{
    public ActionResult NotFound()
    {
        ActionResult result;

        object model = Request.Url.PathAndQuery;

        if (!Request.IsAjaxRequest())
            result = View(model);
        else
            result = PartialView("_NotFound", model);

        return result;
    }
}

(ไม่จำเป็น)

คำอธิบาย:

AFAIK มี 6 กรณีที่แตกต่างกันซึ่งแอป ASP.NET MVC3 สามารถสร้าง 404 ได้

(สร้างโดยอัตโนมัติโดย ASP.NET Framework :)

(1) URL ไม่พบข้อมูลที่ตรงกันในตารางเส้นทาง

(สร้างขึ้นโดยอัตโนมัติโดย ASP.NET MVC Framework :)

(2) URL พบการจับคู่ในตารางเส้นทาง แต่ระบุตัวควบคุมที่ไม่มีอยู่จริง

(3) URL พบการจับคู่ในตารางเส้นทาง แต่ระบุการกระทำที่ไม่มีอยู่จริง

(สร้างขึ้นด้วยตนเอง :)

(4)การดำเนินการส่งคืน HttpNotFoundResult โดยใช้วิธี HttpNotFound ()

(5)การดำเนินการขว้าง HttpException ด้วยรหัสสถานะ 404

(6)การดำเนินการแก้ไขคุณสมบัติ Response.StatusCode ด้วยตนเองเป็น 404

โดยปกติคุณต้องการบรรลุวัตถุประสงค์ 3 ข้อ:

(1)แสดงหน้าข้อผิดพลาด 404 ที่กำหนดเองให้กับผู้ใช้

(2)รักษารหัสสถานะ 404 ในการตอบสนองลูกค้า (สำคัญเป็นพิเศษสำหรับ SEO)

(3)ส่งการตอบกลับโดยตรงโดยไม่ต้องเปลี่ยนเส้นทาง 302

มีหลายวิธีในการพยายามทำสิ่งนี้ให้สำเร็จ:

(1)

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customError>
</system.web>

ปัญหาเกี่ยวกับวิธีแก้ไขปัญหานี้:

  1. ไม่ปฏิบัติตามวัตถุประสงค์ (1) ในกรณี (1), (4), (6)
  2. ไม่ปฏิบัติตามวัตถุประสงค์ (2) โดยอัตโนมัติ จะต้องตั้งโปรแกรมด้วยตนเอง
  3. ไม่เป็นไปตามวัตถุประสงค์ (3)

(2)

<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

ปัญหาเกี่ยวกับวิธีแก้ไขปัญหานี้:

  1. ใช้งานได้กับ IIS 7+ เท่านั้น
  2. ไม่ปฏิบัติตามวัตถุประสงค์ (1) ในกรณี (2), (3), (5)
  3. ไม่ปฏิบัติตามวัตถุประสงค์ (2) โดยอัตโนมัติ จะต้องตั้งโปรแกรมด้วยตนเอง

(3)

<system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

ปัญหาเกี่ยวกับวิธีแก้ไขปัญหานี้:

  1. ใช้งานได้กับ IIS 7+ เท่านั้น
  2. ไม่ปฏิบัติตามวัตถุประสงค์ (2) โดยอัตโนมัติ จะต้องตั้งโปรแกรมด้วยตนเอง
  3. มันปิดกั้นข้อยกเว้น http ระดับแอปพลิเคชัน เช่นไม่สามารถใช้ส่วน customErrors, System.Web.Mvc.HandleErrorAttribute เป็นต้นไม่สามารถแสดงเฉพาะหน้าข้อผิดพลาดทั่วไปได้

(4)

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customError>
</system.web>

และ

<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

ปัญหาเกี่ยวกับวิธีแก้ไขปัญหานี้:

  1. ใช้งานได้กับ IIS 7+ เท่านั้น
  2. ไม่ปฏิบัติตามวัตถุประสงค์ (2) โดยอัตโนมัติ จะต้องตั้งโปรแกรมด้วยตนเอง
  3. ไม่ปฏิบัติตามวัตถุประสงค์ (3) ในกรณี (2), (3), (5)

ผู้ที่เคยมีปัญหากับเรื่องนี้มาก่อนแม้แต่จะพยายามสร้างห้องสมุดของตัวเอง (ดูhttp://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html ) แต่ดูเหมือนว่าวิธีแก้ไขปัญหาก่อนหน้าจะครอบคลุมทุกกรณีโดยไม่มีความซับซ้อนในการใช้ไลบรารีภายนอก


คำตอบที่ดี คุ้มค่ากับการ upvotes อื่น ๆ อีกมากมาย เหตุใดรหัส global.asax ของคุณจึงไม่ทำงาน / อยู่ใน Application_Error
NinjaNye

7
ขอบคุณ! ไม่สามารถทำได้ภายใต้ Application_Error เนื่องจาก 404 ที่ส่งออกมาจากตัวควบคุมอย่างชัดเจนไม่ถือเป็นข้อผิดพลาดใน ASP.NET หากคุณส่งคืน HttpNotFound () จากคอนโทรลเลอร์เหตุการณ์ Application_Error จะไม่มีการทริกเกอร์
Marco

1
ฉันคิดว่าคุณลืมpublic ActionResult NotFound() {}ข้อผิดพลาดของคุณ นอกจากนี้คุณสามารถอธิบายได้ว่าลักษณะ_NotFoundส่วนของคุณสำหรับคำขอ AJAX เป็นอย่างไร
d4n3

2
ด้วย MVC 4 ฉันมักจะMissingMethodException: Cannot create an abstract classมีc.Execute(new RequestContext(new HttpContextWrapper(Context), rd)); ความคิดเห็นอยู่เสมอ
µBio

1
หาก URL "ไม่พบ" มีจุดในเส้นทาง (เช่นexample.com/hi.bob ) ดังนั้น Application_EndRequest จะไม่เริ่มทำงานเลยและฉันจะได้รับหน้า 404 ทั่วไปของ IE
Bob.at.Indigo.Health

13

ฉันชอบโซลูชัน cottsaks และคิดว่ามันอธิบายได้อย่างชัดเจน การเพิ่มเพียงอย่างเดียวของฉันคือการแก้ไขขั้นตอนที่ 2 ดังนี้

public abstract class MyController : Controller
{

    #region Http404 handling

    protected override void HandleUnknownAction(string actionName)
    {
        //if controller is ErrorController dont 'nest' exceptions
        if(this.GetType() != typeof(ErrorController))
        this.InvokeHttp404(HttpContext);
    }

    public ActionResult InvokeHttp404(HttpContextBase httpContext)
    {
        IController errorController = ObjectFactory.GetInstance<ErrorController>();
        var errorRoute = new RouteData();
        errorRoute.Values.Add("controller", "Error");
        errorRoute.Values.Add("action", "Http404");
        errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
        errorController.Execute(new RequestContext(
             httpContext, errorRoute));

        return new EmptyResult();
    }

    #endregion
}

โดยทั่วไปสิ่งนี้จะหยุด URL ที่มีการกระทำที่ไม่ถูกต้องและตัวควบคุมไม่ให้เรียกรูทีนข้อยกเว้นสองครั้ง เช่นสำหรับ URL เช่น asdfsdf / dfgdfgd


4
มันยอดเยี่ยมมาก กรณี "สองครั้ง" เหล่านั้นเริ่มรบกวนฉัน อัปเดตคำตอบของฉัน
Matt Kocaj

โซลูชันข้างต้นทำงานอย่างไรถ้าผู้ใช้ป้อนชื่อคอนโทรลเลอร์และชื่อการกระทำที่ไม่ถูกต้อง
Monojit Sarkar

6

วิธีเดียวที่ฉันจะได้รับ @cottsak วิธีการทำงานสำหรับตัวควบคุมที่ไม่ถูกต้องคือการปรับเปลี่ยนการร้องขอเส้นทางที่มีอยู่ใน CustomControllerFactory เช่น:

public class CustomControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(requestContext, controllerType); 
            else
                return ObjectFactory.GetInstance(controllerType) as Controller;
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                requestContext.RouteData.Values["controller"] = "Error";
                requestContext.RouteData.Values["action"] = "Http404";
                requestContext.RouteData.Values.Add("url", requestContext.HttpContext.Request.Url.OriginalString);

                return ObjectFactory.GetInstance<ErrorController>();
            }
            else
                throw ex;
        }
    }
}

ฉันควรจะพูดถึงว่าฉันใช้ MVC 2.0


คุณรู้ไหมว่าทำไม? (เฉพาะ MVC2 เท่านั้น)
Matt Kocaj

ฉันคิดว่ากุญแจสำคัญคือการแก้ไขคำขอที่มีอยู่แทนที่จะสร้างใหม่ แต่ฉันทำสิ่งนี้มาสักครู่แล้วดังนั้นฉันไม่แน่ใจว่าเป็นแบบนั้น "InvokeHttp404" ไม่ทำงานจากโรงงานคอนโทรลเลอร์
Dave K

ฉันได้อัปเดตคำตอบของฉันในวันนี้ด้วยข้อมูลเฉพาะของ MVC2 คุณช่วยบอกฉันได้ไหมว่าวิธีแก้ปัญหาของฉันตามรายละเอียดด้านบนยังไม่เหมาะกับคุณ?
Matt Kocaj

4

นี่เป็นอีกวิธีหนึ่งที่ใช้เครื่องมือ MVC ซึ่งคุณสามารถจัดการการร้องขอชื่อตัวควบคุมที่ไม่ดีชื่อเส้นทางที่ไม่ดีและเกณฑ์อื่น ๆ ที่คุณเห็นว่าเหมาะสมภายในวิธีการดำเนินการ ส่วนตัวฉันชอบที่จะหลีกเลี่ยงการตั้งค่า web.config ให้มากที่สุดเพราะพวกเขาทำการเปลี่ยนเส้นทาง 302/200 และไม่สนับสนุน ResponseRewrite (Server.Transfer ) โดยใช้มุมมองมีดโกน ฉันต้องการส่งคืน 404 พร้อมหน้าข้อผิดพลาดที่กำหนดเองเพื่อเหตุผลด้าน SEO

บางส่วนเป็นสิ่งใหม่ในเทคนิคของ cottsak ด้านบน

วิธีการแก้ปัญหานี้ยังใช้การตั้งค่า web.config น้อยที่สุดซึ่งรองรับตัวกรองข้อผิดพลาด MVC 3 แทน

การใช้

เพียงแค่โยน HttpException จากแอ็คชันหรือ ActionFilterAttribute ที่กำหนดเอง

Throw New HttpException(HttpStatusCode.NotFound, "[Custom Exception Message Here]")

ขั้นตอนที่ 1

เพิ่มการตั้งค่าต่อไปนี้ใน web.config ของคุณ จำเป็นต้องใช้ HandleErrorAttribute ของ MVC

<customErrors mode="On" redirectMode="ResponseRedirect" />

ขั้นตอนที่ 2

เพิ่ม HandleHttpErrorAttribute แบบกำหนดเองที่คล้ายกับ HandleErrorAttribute ของกรอบ MVC ยกเว้นข้อผิดพลาด HTTP:

<AttributeUsage(AttributeTargets.All, AllowMultiple:=True)>
Public Class HandleHttpErrorAttribute
    Inherits FilterAttribute
    Implements IExceptionFilter

    Private Const m_DefaultViewFormat As String = "ErrorHttp{0}"

    Private m_HttpCode As HttpStatusCode
    Private m_Master As String
    Private m_View As String

    Public Property HttpCode As HttpStatusCode
        Get
            If m_HttpCode = 0 Then
                Return HttpStatusCode.NotFound
            End If
            Return m_HttpCode
        End Get
        Set(value As HttpStatusCode)
            m_HttpCode = value
        End Set
    End Property

    Public Property Master As String
        Get
            Return If(m_Master, String.Empty)
        End Get
        Set(value As String)
            m_Master = value
        End Set
    End Property

    Public Property View As String
        Get
            If String.IsNullOrEmpty(m_View) Then
                Return String.Format(m_DefaultViewFormat, Me.HttpCode)
            End If
            Return m_View
        End Get
        Set(value As String)
            m_View = value
        End Set
    End Property

    Public Sub OnException(filterContext As System.Web.Mvc.ExceptionContext) Implements System.Web.Mvc.IExceptionFilter.OnException
        If filterContext Is Nothing Then Throw New ArgumentException("filterContext")

        If filterContext.IsChildAction Then
            Return
        End If

        If filterContext.ExceptionHandled OrElse Not filterContext.HttpContext.IsCustomErrorEnabled Then
            Return
        End If

        Dim ex As HttpException = TryCast(filterContext.Exception, HttpException)
        If ex Is Nothing OrElse ex.GetHttpCode = HttpStatusCode.InternalServerError Then
            Return
        End If

        If ex.GetHttpCode <> Me.HttpCode Then
            Return
        End If

        Dim controllerName As String = filterContext.RouteData.Values("controller")
        Dim actionName As String = filterContext.RouteData.Values("action")
        Dim model As New HandleErrorInfo(filterContext.Exception, controllerName, actionName)

        filterContext.Result = New ViewResult With {
            .ViewName = Me.View,
            .MasterName = Me.Master,
            .ViewData = New ViewDataDictionary(Of HandleErrorInfo)(model),
            .TempData = filterContext.Controller.TempData
        }
        filterContext.ExceptionHandled = True
        filterContext.HttpContext.Response.Clear()
        filterContext.HttpContext.Response.StatusCode = Me.HttpCode
        filterContext.HttpContext.Response.TrySkipIisCustomErrors = True
    End Sub
End Class

ขั้นตอนที่ 3

เพิ่มกรองเพื่อ GlobalFilterCollection (การGlobalFilters.Filters) Global.asaxใน ตัวอย่างนี้จะกำหนดเส้นทางข้อผิดพลาด InternalServerError (500) ทั้งหมดไปยังมุมมองข้อผิดพลาดที่ใช้ร่วมกัน ( Views/Shared/Error.vbhtml) ข้อผิดพลาด NotFound (404) จะถูกส่งไปยัง ErrorHttp404.vbhtml ในมุมมองที่ใช้ร่วมกันเช่นกัน ฉันได้เพิ่มข้อผิดพลาด 401 ที่นี่เพื่อแสดงให้คุณเห็นว่าสามารถขยายได้อย่างไรสำหรับรหัสข้อผิดพลาด HTTP เพิ่มเติม โปรดทราบว่าสิ่งเหล่านี้ต้องเป็นมุมมองที่ใช้ร่วมกันและทุกคนใช้System.Web.Mvc.HandleErrorInfoวัตถุเป็นแบบจำลอง

filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp401", .HttpCode = HttpStatusCode.Unauthorized})
filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp404", .HttpCode = HttpStatusCode.NotFound})
filters.Add(New HandleErrorAttribute With {.View = "Error"})

ขั้นตอนที่ 4

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

Public Class BaseController
    Inherits System.Web.Mvc.Controller

    Protected Overrides Sub HandleUnknownAction(actionName As String)
        Me.ActionInvoker.InvokeAction(Me.ControllerContext, "Unknown")
    End Sub

    Public Function Unknown() As ActionResult
        Throw New HttpException(HttpStatusCode.NotFound, "The specified controller or action does not exist.")
        Return New EmptyResult
    End Function
End Class

ขั้นตอนที่ 5

สร้างการแทนที่ ControllerFactory และแทนที่ในไฟล์ Global.asax ของคุณใน Application_Start ขั้นตอนนี้ช่วยให้เราสามารถเพิ่มข้อยกเว้น HTTP 404 เมื่อมีการระบุชื่อตัวควบคุมที่ไม่ถูกต้อง

Public Class MyControllerFactory
    Inherits DefaultControllerFactory

    Protected Overrides Function GetControllerInstance(requestContext As System.Web.Routing.RequestContext, controllerType As System.Type) As System.Web.Mvc.IController
        Try
            Return MyBase.GetControllerInstance(requestContext, controllerType)
        Catch ex As HttpException
            Return DependencyResolver.Current.GetService(Of BaseController)()
        End Try
    End Function
End Class

'In Global.asax.vb Application_Start:

controllerBuilder.Current.SetControllerFactory(New MyControllerFactory)

ขั้นตอนที่ 6

รวมเส้นทางพิเศษใน RoutTable ของคุณเส้นทางสำหรับการกระทำที่ไม่รู้จัก BaseController สิ่งนี้จะช่วยให้เราเพิ่ม 404 ในกรณีที่ผู้ใช้เข้าถึงคอนโทรลเลอร์ที่ไม่รู้จักหรือการกระทำที่ไม่รู้จัก

'BaseController
routes.MapRoute( _
    "Unknown", "BaseController/{action}/{id}", _
    New With {.controller = "BaseController", .action = "Unknown", .id = UrlParameter.Optional} _
)

สรุป

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

ฉันจะเพิ่มภาพหน้าจอของชื่อตัวควบคุมที่ไม่ถูกต้องชื่อการกระทำและ 404 ที่กำหนดเองที่ยกมาจากการกระทำ Home / TriggerNotFound ถ้าฉันได้รับคะแนนมากพอที่จะโพสต์หนึ่ง =) Fiddler ส่งกลับข้อความ 404 เมื่อฉันเข้าถึง URL ต่อไปนี้โดยใช้โซลูชันนี้:

/InvalidController
/Home/InvalidRoute
/InvalidController/InvalidRoute
/Home/TriggerNotFound

โพสต์ของ cottsak ข้างต้นและบทความเหล่านี้มีการอ้างอิงที่ดี


อืมฉันไม่สามารถทำงานนี้: The IControllerFactory 'aaa.bbb.CustomControllerFactory' did not return a controller for the name '123'.- ความคิดใด ๆ ที่ฉันจะได้รับนั่น?
enashnash

redirectMode = "ResponseRedirect" สิ่งนี้จะส่งคืน 302 การค้นพบ + 200 การตกลงซึ่งไม่ดีสำหรับ SEO!
PussInBoots

4

โซลูชันแบบย่อของฉันที่ใช้งานได้กับพื้นที่ควบคุมและการกระทำที่ไม่ได้จัดการ:

  1. สร้างมุมมอง 404.cshtml

  2. สร้างคลาสพื้นฐานสำหรับตัวควบคุมของคุณ:

    public class Controller : System.Web.Mvc.Controller
    {
        protected override void HandleUnknownAction(string actionName)
        {
            Http404().ExecuteResult(ControllerContext);
        }
    
        protected virtual ViewResult Http404()
        {
            Response.StatusCode = (int)HttpStatusCode.NotFound;
            return View("404");
        }
    }
  3. สร้างโรงงานคอนโทรลเลอร์ที่กำหนดเองซึ่งส่งคืนคอนโทรลเลอร์พื้นฐานของคุณเป็นทางเลือก:

    public class ControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType != null)
                return base.GetControllerInstance(requestContext, controllerType);
    
            return new Controller();
        }
    }
  4. เพิ่มApplication_Start()ในบรรทัดต่อไปนี้:

    ControllerBuilder.Current.SetControllerFactory(typeof(ControllerFactory));

3

ใน MVC4 WebAPI 404 สามารถจัดการได้ด้วยวิธีดังต่อไปนี้

หลักสูตร APICONTROLLER

    // GET /api/courses/5
    public HttpResponseMessage<Courses> Get(int id)
    {
        HttpResponseMessage<Courses> resp = null;

        var aCourse = _courses.Where(c => c.Id == id).FirstOrDefault();

        resp = aCourse == null ? new HttpResponseMessage<Courses>(System.Net.HttpStatusCode.NotFound) : new HttpResponseMessage<Courses>(aCourse);

        return resp;
    }

หน้าแรกควบคุม

public ActionResult Course(int id)
{
    return View(id);
}

ดู

<div id="course"></div>
<script type="text/javascript">
    var id = @Model;
    var course = $('#course');
    $.ajax({    
        url: '/api/courses/' + id,
        success: function (data) {
            course.text(data.Name);
        },
        statusCode: {
            404: function() 
            {
                course.text('Course not available!');    
            }
        }
    });
</script>

GLOBAL

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

ผล

ป้อนคำอธิบายรูปภาพที่นี่


2

ลอง NotFoundMVC บน nuget มันใช้งานได้ไม่มีการตั้งค่า


http://localhost/Views/Shared/NotFound.cshtmlไม่ส่งผลให้หน้า 404 ที่กำหนดเอง
Dan Friedman

มันง่ายมากที่จะปรับแต่ง คุณสามารถเข้าถึง URL ที่ร้องขอและผู้อ้างอิงเพื่อให้คุณสามารถทำสิ่งที่คุณต้องการ ฉันใช้แพ็คเกจนี้และมันใช้งานได้ดีจริงๆ
Avrohom Yisroel

นี่เป็นแพคเกจที่ยอดเยี่ยมโดยที่คุณจะไม่ต้องใช้แอคชัน <ActionResult> การทำงานแบบ async (หรือ async อื่น ๆ ที่คล้ายกัน) ใน MVC 5 นี่เป็นสถานการณ์ที่ไม่สมบูรณ์ มีทางแยกให้ GitHub หลบเลี่ยงสิ่งนั้น แต่สำหรับฉันมันไม่ใช่ไม่
Stargazer

2

ทางออกของฉันในกรณีที่มีคนพบว่ามีประโยชน์

ใน Web.config:

<system.web>
    <customErrors mode="On" defaultRedirect="Error" >
      <error statusCode="404" redirect="~/Error/PageNotFound"/>
    </customErrors>
    ...
</system.web>

ในControllers/ErrorController.cs:

public class ErrorController : Controller
{
    public ActionResult PageNotFound()
    {
        if(Request.IsAjaxRequest()) {
            Response.StatusCode = (int)HttpStatusCode.NotFound;
            return Content("Not Found", "text/plain");
        }

        return View();
    }
}

เพิ่มPageNotFound.cshtmlในSharedโฟลเดอร์และนั่นคือมัน


2
นี่ไม่ใช่ปัญหาการเปลี่ยนเส้นทาง 302 จากนั้นสถานะ 200 (OK) ไปยังลูกค้าหรือไม่ พวกเขาควรจะได้รับสถานะ 404 หรือไม่?
Sam

@ Konamiman คุณแน่ใจหรือไม่ว่าบรรทัดในรหัสของคุณควรอ่านmodel.RequestedUrl = Request.Url.OriginalString.Contains(url) & Request.Url.OriginalString != url ? Request.Url.OriginalString : url;ไม่ใช่model.RequestedUrl = Request.Url.OriginalString.Contains(url) && Request.Url.OriginalString != url ? Request.Url.OriginalString : url;(& แทนที่จะเป็น &&)?
Jean-François Beauchamp

2

สำหรับฉันแล้วดูเหมือนว่าการCustomErrorsกำหนดค่ามาตรฐานควรจะใช้งานได้แต่เนื่องจากการพึ่งพาServer.Transferดูเหมือนว่าการใช้งานภายในของResponseRewriteไม่สามารถทำงานร่วมกับ MVC ได้

สิ่งนี้ให้ความรู้สึกเหมือนฟังก์ชั่นการใช้งานที่ชัดเจนสำหรับฉันดังนั้นฉันจึงตัดสินใจใช้คุณลักษณะนี้อีกครั้งโดยใช้โมดูล HTTP โซลูชันด้านล่างช่วยให้คุณสามารถจัดการกับรหัสสถานะ HTTP (รวมถึง 404) โดยเปลี่ยนเส้นทางไปยังเส้นทาง MVC ที่ถูกต้องเช่นเดียวกับที่คุณทำตามปกติ

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
    <error statusCode="404" redirect="404.aspx" />
    <error statusCode="500" redirect="~/MVCErrorPage" />
</customErrors>

สิ่งนี้ได้รับการทดสอบบนแพลตฟอร์มต่อไปนี้

  • MVC4 ในโหมด Pipeline รวม (IIS Express 8)
  • MVC4 ในโหมดคลาสสิก (VS เซิร์ฟเวอร์สำหรับการพัฒนา Cassini)
  • MVC4 ในโหมดคลาสสิค (IIS6)

ประโยชน์ที่ได้รับ

  • โซลูชันทั่วไปที่สามารถนำไปวางในโครงการ MVC ใดก็ได้
  • เปิดใช้งานการสนับสนุนสำหรับการกำหนดค่าข้อผิดพลาดที่กำหนดเองแบบดั้งเดิม
  • ทำงานได้ทั้งใน Pipeline และ Classic

การแก้ไขปัญหา

namespace Foo.Bar.Modules {

    /// <summary>
    /// Enables support for CustomErrors ResponseRewrite mode in MVC.
    /// </summary>
    public class ErrorHandler : IHttpModule {

        private HttpContext HttpContext { get { return HttpContext.Current; } }
        private CustomErrorsSection CustomErrors { get; set; }

        public void Init(HttpApplication application) {
            System.Configuration.Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~");
            CustomErrors = (CustomErrorsSection)configuration.GetSection("system.web/customErrors");

            application.EndRequest += Application_EndRequest;
        }

        protected void Application_EndRequest(object sender, EventArgs e) {

            // only handle rewrite mode, ignore redirect configuration (if it ain't broke don't re-implement it)
            if (CustomErrors.RedirectMode == CustomErrorsRedirectMode.ResponseRewrite && HttpContext.IsCustomErrorEnabled) {

                int statusCode = HttpContext.Response.StatusCode;

                // if this request has thrown an exception then find the real status code
                Exception exception = HttpContext.Error;
                if (exception != null) {
                    // set default error status code for application exceptions
                    statusCode = (int)HttpStatusCode.InternalServerError;
                }

                HttpException httpException = exception as HttpException;
                if (httpException != null) {
                    statusCode = httpException.GetHttpCode();
                }

                if ((HttpStatusCode)statusCode != HttpStatusCode.OK) {

                    Dictionary<int, string> errorPaths = new Dictionary<int, string>();

                    foreach (CustomError error in CustomErrors.Errors) {
                        errorPaths.Add(error.StatusCode, error.Redirect);
                    }

                    // find a custom error path for this status code
                    if (errorPaths.Keys.Contains(statusCode)) {
                        string url = errorPaths[statusCode];

                        // avoid circular redirects
                        if (!HttpContext.Request.Url.AbsolutePath.Equals(VirtualPathUtility.ToAbsolute(url))) {

                            HttpContext.Response.Clear();
                            HttpContext.Response.TrySkipIisCustomErrors = true;

                            HttpContext.Server.ClearError();

                            // do the redirect here
                            if (HttpRuntime.UsingIntegratedPipeline) {
                                HttpContext.Server.TransferRequest(url, true);
                            }
                            else {
                                HttpContext.RewritePath(url, false);

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

                            // return the original status code to the client
                            // (this won't work in integrated pipleline mode)
                            HttpContext.Response.StatusCode = statusCode;

                        }
                    }

                }

            }

        }

        public void Dispose() {

        }


    }

}

การใช้

รวมสิ่งนี้เป็นโมดูล HTTP สุดท้ายใน web.config ของคุณ

  <system.web>
    <httpModules>
      <add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
    </httpModules>
  </system.web>

  <!-- IIS7+ -->
  <system.webServer>
    <modules>
      <add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
    </modules>
  </system.webServer>

สำหรับผู้ที่สนใจคุณจะสังเกตเห็นว่าในโหมด Integrated Pipeline สิ่งนี้จะตอบสนองต่อ HTTP 200 เสมอเนื่องจากวิธีการServer.TransferRequestทำงาน ในการส่งคืนรหัสข้อผิดพลาดที่เหมาะสมฉันใช้ตัวควบคุมข้อผิดพลาดต่อไปนี้

public class ErrorController : Controller {

    public ErrorController() { }

    public ActionResult Index(int id) {
        // pass real error code to client
        HttpContext.Response.StatusCode = id;
        HttpContext.Response.TrySkipIisCustomErrors = true;

        return View("Errors/" + id.ToString());
    }

}

2

การจัดการกับข้อผิดพลาดใน ASP.NET MVC เป็นเพียงความเจ็บปวดในก้น ฉันลองใช้คำแนะนำมากมายในหน้านี้และคำถามและเว็บไซต์อื่น ๆ และไม่มีอะไรดี หนึ่งข้อเสนอแนะคือการจัดการข้อผิดพลาดในweb.configภายในsystem.webserverแต่ที่เพิ่งกลับหน้าว่างผลตอบแทนที่หน้าว่าง

เป้าหมายของฉันเมื่อมากับโซลูชันนี้คือการ;

  • ไม่ได้ทำการกู้คืน
  • ส่งคืนรหัสสถานะที่เหมาะสมไม่ใช่ 200 / Ok เช่นการจัดการข้อผิดพลาดเริ่มต้น

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

1 .Add ต่อไปนี้เพื่อsystem.webส่วน

   <system.web>
     <customErrors mode="On" redirectMode="ResponseRewrite">
      <error statusCode="404"  redirect="~/Error/404.aspx" />
      <error statusCode="500" redirect="~/Error/500.aspx" />
     </customErrors>
    <system.web>

ด้านบนจัดการ URL ใด ๆ ที่ไม่ได้จัดการโดยroute.configและข้อยกเว้นที่ไม่สามารถจัดการได้โดยเฉพาะอย่างยิ่งที่พบในมุมมอง แจ้งให้ทราบล่วงหน้าฉันใช้aspxไม่ใช่html นี่คือเพื่อให้ฉันสามารถเพิ่มรหัสการตอบสนองในรหัสที่อยู่เบื้องหลัง

2 . สร้างโฟลเดอร์ชื่อError (หรืออะไรก็ได้ที่คุณต้องการ) ที่รูทของโปรเจคและเพิ่ม webforms สองอัน ด้านล่างเป็นหน้า 404 ของฉัน

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="404.aspx.cs" Inherits="Myapp.Error._404" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title >Page Not found</title>
    <link href="<%=ResolveUrl("~/Content/myapp.css")%>" rel="stylesheet" />
</head>
<body>
    <div class="top-nav">
      <a runat="server" class="company-logo" href="~/"></a>
    </div>
    <div>
        <h1>404 - Page Not found</h1>
        <p>The page you are looking for cannot be found.</p>
        <hr />
        <footer></footer>
    </div>
</body>
</html>

และในรหัสด้านหลังฉันตั้งค่ารหัสการตอบสนอง

protected void Page_Load(object sender, EventArgs e)
{
    Response.StatusCode = 404;
}

ทำเช่นเดียวกันกับหน้า 500

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

protected ActionResult ShowNotFound()
{
    return ShowNotFound("Page not found....");
}

protected ActionResult ShowNotFound(string message)
{
    return ShowCustomError(HttpStatusCode.NotFound, message);
}

protected ActionResult ShowServerError()
{
    return ShowServerError("Application error....");
}

protected ActionResult ShowServerError(string message)
{
    return ShowCustomError(HttpStatusCode.InternalServerError, message);
}

protected ActionResult ShowNotAuthorized()
{
    return ShowNotAuthorized("You are not allowed ....");

}

protected ActionResult ShowNotAuthorized(string message)
{
    return ShowCustomError(HttpStatusCode.Forbidden, message);
}

protected ActionResult ShowCustomError(HttpStatusCode statusCode, string message)
{
    Response.StatusCode = (int)statusCode;
    string title = "";
    switch (statusCode)
    {
        case HttpStatusCode.NotFound:
            title = "404 - Not found";
            break;
        case HttpStatusCode.Forbidden:
            title = "403 - Access Denied";
            break;
        default:
            title = "500 - Application Error";
            break;
    }
    ViewBag.Title = title;
    ViewBag.Message = message;
    return View("CustomError");
}

4.เพิ่ม CustomError.cshtml ไปยังโฟลเดอร์มุมมองที่ใช้ร่วมกันของคุณ ด้านล่างเป็นของฉัน;

<h1>@ViewBag.Title</h1>
<br />
<p>@ViewBag.Message</p>

ในคอนโทรลเลอร์แอปพลิเคชันของคุณคุณสามารถทำสิ่งนี้ได้

public class WidgetsController : ControllerBase
{
  [HttpGet]
  public ActionResult Edit(int id)
  {
    Try
    {
       var widget = db.getWidgetById(id);
       if(widget == null)
          return ShowNotFound();
          //or return ShowNotFound("Invalid widget!");
       return View(widget);
    }
    catch(Exception ex)
    {
       //log error
       logger.Error(ex)
       return ShowServerError();
    }
  }
}

ตอนนี้สำหรับข้อแม้ มันจะไม่จัดการข้อผิดพลาดไฟล์คงที่ ดังนั้นหากคุณมีเส้นทางเช่นexample.com/widgetsและผู้ใช้เปลี่ยนเป็นexample.com/widgets.htmlพวกเขาจะได้รับหน้าข้อผิดพลาด IIS เริ่มต้นดังนั้นคุณต้องจัดการข้อผิดพลาดระดับ IIS ด้วยวิธีอื่น


1

โพสต์คำตอบเนื่องจากความคิดเห็นของฉันยาวเกินไป ...

มันเป็นทั้งความคิดเห็นและคำถามที่โพสต์ / คำตอบของยูนิคอร์น:

https://stackoverflow.com/a/7499406/687549

ฉันชอบคำตอบนี้มากกว่าคำตอบอื่น ๆ เพราะความเรียบง่ายและความจริงที่ว่าบางคนใน Microsoft ได้รับการพิจารณา ฉันมีสามคำถามอย่างไรก็ตามและถ้าพวกเขาสามารถตอบได้ฉันจะเรียกคำตอบนี้ว่าจอกศักดิ์สิทธิ์ของคำตอบข้อผิดพลาด 404/500 ทั้งหมดใน interwebs สำหรับแอพ ASP.NET MVC (x)

@ Pure.Krome

  1. คุณสามารถปรับปรุงคำตอบของคุณด้วยเนื้อหา SEO จากความคิดเห็นที่ชี้ให้เห็นโดย GWB (ไม่เคยมีการพูดถึงเรื่องนี้ในคำตอบของคุณ) - <customErrors mode="On" redirectMode="ResponseRewrite">และ<httpErrors errorMode="Custom" existingResponse="Replace">?

  2. คุณสามารถถามเพื่อนในทีม ASP.NET ของคุณได้ไหมถ้าทำอย่างนั้นจะดีกว่าถ้าได้รับการยืนยัน - อาจเป็นเรื่องใหญ่ที่ไม่มีการเปลี่ยนแปลงredirectModeและexistingResponseด้วยวิธีนี้เพื่อให้สามารถเล่นกับ SEO ได้อย่างดี!

  3. คุณสามารถเพิ่มการชี้แจงบางรอบทุกสิ่ง ( customErrors redirectMode="ResponseRewrite", customErrors redirectMode="ResponseRedirect", httpErrors errorMode="Custom" existingResponse="Replace", ลบcustomErrorsโดยสิ้นเชิงเป็นคนแนะนำ) หลังจากการพูดคุยกับเพื่อน ๆ ของคุณที่ไมโครซอฟท์?

อย่างที่ฉันพูด มันจะยิ่งใหญ่ถ้าเราจะทำให้คำตอบของคุณสมบูรณ์ยิ่งขึ้นเพราะนี่เป็นคำถามที่ได้รับความนิยมอย่างมากด้วยจำนวนการดู 54,000 ครั้งขึ้นไป

อัปเดต : คำตอบยูนิคอร์นทำ 302 Found และ 200 OK และไม่สามารถเปลี่ยนเป็นคืน 404 โดยใช้เส้นทางเท่านั้น ต้องเป็นไฟล์จริงที่ไม่ได้เป็น MVC: ish ดังนั้นไปยังทางออกอื่น เลวร้ายเกินไปเพราะนี่ดูเหมือนจะเป็น MVC ขั้นสูงสุด: ฉันตอบคำถามนี้ไปไกลแล้ว


1

การเพิ่มโซลูชันของฉันซึ่งเกือบจะเหมือนกับของเฮอร์แมนกานด้วยริ้วรอยเล็ก ๆ เพื่อให้มันใช้ได้กับโครงการของฉัน

สร้างตัวควบคุมข้อผิดพลาดที่กำหนดเอง:

public class Error404Controller : BaseController
{
    [HttpGet]
    public ActionResult PageNotFound()
    {
        Response.StatusCode = 404;
        return View("404");
    }
}

จากนั้นสร้างโรงงานตัวควบคุมที่กำหนดเอง:

public class CustomControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        return controllerType == null ? new Error404Controller() : base.GetControllerInstance(requestContext, controllerType);
    }
}

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

protected override void HandleUnknownAction(string actionName)
{
    var errorRoute = new RouteData();
    errorRoute.Values.Add("controller", "Error404");
    errorRoute.Values.Add("action", "PageNotFound");
    new Error404Controller().Execute(new RequestContext(HttpContext, errorRoute));
}

และนั่นคือมัน ไม่จำเป็นต้องเปลี่ยน Web.config


1

1) สร้างคลาสคอนโทรลเลอร์แบบนามธรรม

public abstract class MyController:Controller
{
    public ActionResult NotFound()
    {
        Response.StatusCode = 404;
        return View("NotFound");
    }

    protected override void HandleUnknownAction(string actionName)
    {
        this.ActionInvoker.InvokeAction(this.ControllerContext, "NotFound");
    }
    protected override void OnAuthorization(AuthorizationContext filterContext) { }
}  

2) สืบทอดจากคลาสนามธรรมนี้ในตัวควบคุมทั้งหมดของคุณ

public class HomeController : MyController
{}  

3) และเพิ่มมุมมองชื่อ "NotFound" ในโฟลเดอร์ View-Shared


0

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

@Marco ชี้ให้เห็นกรณีต่างๆที่สามารถเกิดขึ้นได้ 404 ฉันตรวจสอบวิธีการที่ฉันรวบรวมจากรายการนั้น นอกเหนือจากรายการข้อกำหนดของเขาฉันยังเพิ่มอีกหนึ่งรายการ

  • การแก้ปัญหาควรจะสามารถจัดการ MVC เช่นเดียวกับการโทร AJAX / WebAPI ในลักษณะที่เหมาะสมที่สุด (เช่นหาก 404 เกิดขึ้นใน MVC ควรแสดงหน้าไม่พบและหาก 404 เกิดขึ้นใน WebAPI ไม่ควรจี้การตอบสนอง XML / JSON เพื่อให้ Javascript ที่ใช้งานสามารถแยกวิเคราะห์ได้ง่าย)

วิธีนี้เป็น 2 เท่า:

ส่วนแรกของมันมาจาก @Guillaume ที่https://stackoverflow.com/a/27354140/2310818 โซลูชันของพวกเขาดูแล 404 ที่เกิดจากเส้นทางที่ไม่ถูกต้องตัวควบคุมที่ไม่ถูกต้องและการกระทำที่ไม่ถูกต้อง

แนวคิดคือการสร้าง WebForm จากนั้นเรียกใช้แอคชัน NotFound ของ MVC Errors Controller ของคุณ มันทำสิ่งเหล่านี้โดยไม่มีการเปลี่ยนเส้นทางดังนั้นคุณจะไม่เห็น 302 ใน Fiddler URL ดั้งเดิมจะได้รับการสงวนไว้ซึ่งทำให้โซลูชันนี้ยอดเยี่ยม!


ส่วนที่สองของมันมาจาก @ เยอรมันhttps://stackoverflow.com/a/5536676/2310818 โซลูชันของพวกเขาดูแลการกระทำของคุณที่ส่งคืน 404 ในรูปแบบของ HttpNotFoundResult () หรือโยน HttpException () ใหม่!

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


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


0

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

 <system.web>
    <customErrors mode="On" redirectMode="ResponseRewrite">
      <error statusCode="404" redirect="~/PageNotFound.aspx"/>
    </customErrors>
  </system.web>
<system.webServer>
    <httpErrors errorMode="Custom">
      <remove statusCode="404"/>
      <error statusCode="404" path="/PageNotFound.html" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

ฉันพบบทความนี้มีประโยชน์มากควรอ่านในครั้งเดียว Custome error page-Ben Foster

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