asp.net mvc - एएसपी.नेट एमवीसी में मैं 404 को कैसे ठीक से संभाल सकता हूं?




asp.net-mvc http-status-code-404 (13)

404 के लिए आवश्यकताएँ

404 समाधान के लिए मेरी आवश्यकताएं निम्नलिखित हैं और नीचे मैं दिखाता हूं कि मैं इसे कैसे कार्यान्वित करता हूं:

  • मैं खराब कार्यों के साथ मिलान किए गए मार्गों को संभालना चाहता हूं
  • मैं खराब नियंत्रकों के साथ मिलान किए गए मार्गों को संभालना चाहता हूं
  • मैं अन-मिलान वाले मार्गों को नियंत्रित करना चाहता हूं (मनमानी यूआरएल जो मेरा ऐप समझ में नहीं आ रहा है) - मैं नहीं चाहता कि ये ग्लोबल.एक्सएक्स या आईआईएस तक बुलबुले हो जाएं क्योंकि तब मैं अपने एमवीसी ऐप में ठीक से रीडायरेक्ट नहीं कर सकता
  • मैं उपर्युक्त, कस्टम 404s जैसे ही संभालने का एक तरीका चाहता हूं - जैसे किसी ऑब्जेक्ट के लिए एक आईडी सबमिट की जाती है जो मौजूद नहीं है (शायद हटाया गया हो)
  • मैं चाहता हूं कि मेरे सभी 404 एमवीसी व्यू (एक स्थिर पृष्ठ नहीं) लौटाएं, जिसके लिए यदि आवश्यक हो तो मैं अधिक डेटा पंप कर सकता हूं ( codinghorror.com/blog/2007/03/… ) और उन्हें HTTP 404 स्टेटस कोड वापस करना होगा

उपाय

मुझे लगता है कि आपको ग्लोबल.एक्सएक्स में ग्लोबल.एक्सएक्स को उच्च चीजों के लिए सहेजना चाहिए, जैसे अनचाहे अपवाद और लॉगिंग (जैसे जै जैकी का जवाब दिखाता है) लेकिन 404 हैंडलिंग नहीं। यही कारण है कि मेरा सुझाव ग्लोबल.एक्सएक्स फ़ाइल से 404 सामान रखता है।

चरण 1: 404-त्रुटि तर्क के लिए एक आम जगह है

यह रखरखाव के लिए एक अच्छा विचार है। एक ErrorController उपयोग करें ताकि आपके codinghorror.com/blog/2007/03/… भविष्य में सुधार आसानी से अनुकूलित हो सकें। साथ ही, सुनिश्चित करें कि आपकी प्रतिक्रिया में 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

एएसपी.नेट एमवीसी में 404 एस कई स्थानों पर पकड़ा जाना चाहिए। पहला HandleUnknownAction

InvokeHttp404 विधि ErrorController और हमारी नई Http404 कार्रवाई को पुनः रूटिंग के लिए एक सामान्य स्थान बनाता है। DRY सोचो!

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: अपने कंट्रोलर फैक्ट्री में निर्भरता इंजेक्शन का उपयोग करें और 404 एचटीपीएक्सप्शन को तार दें

ऐसा (यह स्ट्रक्चर मैप नहीं होना चाहिए):

एमवीसी 1.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;
    }
}

एमवीसी 2.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 हैंडलर को पसंद करता हूं।

404 के लिए यह दूसरा स्थान है।

चरण 4: ग्लोबल.एक्सएक्स को यूआरएल के लिए एक नोटफॉउंड रूट जोड़ें जो आपके ऐप में पार्स करने में विफल रहता है

इस मार्ग को हमारी Http404 कार्रवाई को इंगित करना चाहिए। ध्यान दें कि url परम एक सापेक्ष यूआरएल होगा क्योंकि रूटिंग इंजन यहां डोमेन भाग को अलग कर रहा है? यही कारण है कि हमारे पास चरण 1 में सभी सशर्त यूआरएल तर्क हैं।

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

यह एक एमवीसी ऐप में 404 को पकड़ने वाला तीसरा और अंतिम स्थान है जिसे आप स्वयं नहीं बुलाते हैं। यदि आप यहां बेजोड़ मार्ग नहीं पकड़ते हैं तो एमवीसी समस्या को एएसपी.नेट (ग्लोबल.एएसएक्स) तक पास कर देगी और आप वास्तव में इस स्थिति में नहीं चाहते हैं।

चरण 5: अंत में, 404s का आह्वान करें जब आपका ऐप कुछ नहीं ढूंढ सके

जैसे कि मेरे ऋण नियंत्रक को एक खराब आईडी सबमिट की जाती है ( 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);
    }

यह अच्छा होगा अगर यह कम कोड वाले कम स्थानों में लगाया जा सकता है लेकिन मुझे लगता है कि यह समाधान अधिक रखरखाव योग्य, अधिक टेस्टेबल और काफी व्यावहारिक है।

अब तक की प्रतिक्रिया के लिए धन्यवाद। मुझे और अधिक मिलना अच्छा लगेगा।

नोट: इसे मेरे मूल उत्तर से महत्वपूर्ण रूप से संपादित किया गया है लेकिन उद्देश्य / आवश्यकताएं समान हैं - यही कारण है कि मैंने एक नया उत्तर नहीं जोड़ा है

मैं बस एएसपी.नेट एमवीसी पर शुरू कर रहा हूं इसलिए मेरे साथ भालू। मैंने इस साइट और कई अन्य लोगों के आसपास खोज की है और इसके कुछ कार्यान्वयन देखा है।

संपादित करें: मैं उल्लेख करना भूल गया कि मैं आरसी 2 का उपयोग कर रहा हूं

यूआरएल रूटिंग का उपयोग करना:

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

उपर्युक्त इस तरह के अनुरोधों का ख्याल रखना प्रतीत होता है (आरंभिक एमवीसी प्रोजेक्ट द्वारा डिफ़ॉल्ट रूट टेबल सेटअप को मानते हुए): "/ blah / blah / blah / blah"

नियंत्रक में हैंडल अज्ञात एक्शन () को ओवरराइड करना:

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

हालांकि पिछली रणनीतियों को खराब / अज्ञात नियंत्रक के अनुरोध को संभाल नहीं है। उदाहरण के लिए, मेरे पास "/ IDoNotExist" नहीं है, अगर मैं इसका अनुरोध करता हूं तो मुझे वेब सर्वर से सामान्य 404 पृष्ठ मिलता है और यदि मैं रूटिंग + ओवरराइड का उपयोग करता हूं तो मेरा 404 नहीं।

तो आखिरकार, मेरा सवाल यह है: क्या एमवीसी फ्रेमवर्क में किसी रूट या कुछ और का उपयोग करके इस प्रकार के अनुरोध को पकड़ने का कोई तरीका है?

या मुझे अपने 404 हैंडलर के रूप में Web.Config customErrors का उपयोग करने के लिए बस डिफ़ॉल्ट करना चाहिए और यह सब भूल जाओ? मुझे लगता है कि अगर मैं कस्टमर के साथ जाता हूं तो मुझे वेब के कारण जेनेरिक 404 पेज / व्यू के बाहर / दृश्यों को स्टोर करना होगा। सीधे पहुंच पर कॉन्फिग प्रतिबंध। वैसे भी किसी भी सर्वोत्तम प्रथाओं या मार्गदर्शन की सराहना की जाती है।


त्वरित उत्तर / टीएल; डीआर

आलसी लोगों के लिए वहां:

Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0

फिर global.asax से इस लाइन को हटा दें

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

और यह केवल आईआईएस 7 + और आईआईएस एक्सप्रेस के लिए है।

यदि आप कैसिनी का उपयोग कर रहे हैं .. ठीक है .. उम .. एर .. अजीब ...

लंबा, समझाया उत्तर

मुझे पता है कि इसका उत्तर दिया गया है। लेकिन जवाब वास्तव में सरल है (वास्तव में इसका जवाब देने के लिए डेविड फाउलर और डेमियन एडवर्ड्स को उत्साहित करता है)।

कुछ भी कस्टम करने की जरूरत नहीं है

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>

अब उन राउट्स का सावधानीपूर्वक ध्यान दें जिन्हें मैंने उपयोग करने का निर्णय लिया है। आप कुछ भी उपयोग कर सकते हैं, लेकिन मेरे मार्ग हैं

  • /NotFound <- 404 नहीं मिला, त्रुटि पृष्ठ।
  • /ServerError <- किसी अन्य त्रुटि के लिए, मेरे कोड में होने वाली त्रुटियां शामिल करें। यह एक 500 आंतरिक सर्वर त्रुटि है

देखें कि <system.web> में पहला अनुभाग केवल एक कस्टम प्रविष्टि कैसे है? statusCode="404" प्रविष्टि? मैंने केवल एक स्टेटस कोड सूचीबद्ध किया है क्योंकि 500 Server Error सहित अन्य सभी त्रुटियां (यानी उन अजीब त्रुटि जो तब होती हैं जब आपके कोड में बग होता है और उपयोगकर्ता के अनुरोध को क्रैश करता है) .. अन्य सभी त्रुटियों को सेटिंग डिफ़ॉल्ट द्वारा निर्देशित किया जाता है। defaultRedirect="/ServerError" .. जो कहता है, यदि आप 404 पृष्ठ नहीं पाए हैं, तो कृपया मार्ग /ServerError

ठीक। वह रास्ते से बाहर है .. अब global.asax में सूचीबद्ध मेरे मार्गों के लिए

चरण 2 - ग्लोबल.एक्सएक्स में मार्ग बनाना

मेरा पूरा मार्ग अनुभाग यहाँ है ..

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! बोनस आपके लिए मार्ग अनदेखा करें!) फिर (और ऑर्डर यहां प्रभावशाली है), मेरे पास मेरे दो स्पष्ट त्रुटि हैंडलिंग मार्ग हैं .. इसके बाद किसी अन्य मार्ग के बाद। इस मामले में, डिफ़ॉल्ट एक। बेशक, मेरे पास और भी है, लेकिन यह मेरी वेबसाइट के लिए विशेष है। बस सुनिश्चित करें कि त्रुटि मार्ग सूची के शीर्ष पर हैं। आदेश जरूरी है

अंत में, जब हम अपनी global.asax फ़ाइल के अंदर हैं, हम वैश्विक स्तर पर हैंडलरर विशेषता पंजीकृत नहीं करते हैं। नहीं, नहीं, नहीं सर। नड्डा। नहीं। निएन। नकारात्मक। 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 फ्रेमवर्क में निर्मित त्रुटियों को पहले से ही संभाल रहा है और हमने एक त्रुटि को संभालने के लिए हमें जो भी बकवास करने की आवश्यकता है उसे निर्दिष्ट किया है :) यह इस विधि में है!

इसके बाद, मेरे पास दो क्रिया विधियां हैं I वहां कुछ भी मुश्किल नहीं है। अगर आप कोई अपवाद जानकारी दिखाना चाहते हैं, तो आप उस जानकारी को प्राप्त करने के लिए Server.GetLastError() का उपयोग कर सकते हैं।

बोनस डब्ल्यूटीएफ: हाँ, मैंने त्रुटि प्रबंधन का परीक्षण करने के लिए एक तीसरी क्रिया विधि बनाई है।

चरण 4 - दृश्य बनाएं

और अंत में, दो विचार बनाएं। इस नियंत्रक के लिए, सामान्य दृश्य स्थान में एम रखें।

बोनस टिप्पणियां

  • आपको Application_Error(object sender, EventArgs e) आवश्यकता नहीं है Application_Error(object sender, EventArgs e)
  • उपर्युक्त कदम सभी Elmah साथ 100% पूरी तरह से काम करते हैं। Elmah fraking wroxs!

और वह, मेरे दोस्त, यह होना चाहिए।

अब, इसे पढ़ने के लिए बधाई और पुरस्कार के रूप में एक यूनिकॉर्न है!


एएसपी.नेट एमवीसी कस्टम 404 पृष्ठों का बहुत अच्छा समर्थन नहीं करता है। कस्टम कंट्रोलर फैक्ट्री, कैच-ऑल रूट, HandleUnknownAction साथ बेस कंट्रोलर क्लास - HandleUnknownAction !

आईआईएस कस्टम त्रुटि पेज अब तक बेहतर विकल्प हैं:

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();
    }
}

नमूना परियोजना


कोड http://blogs.microsoft.co.il/blogs/shay/archive/2009/03/06/real-world-error-hadnling-in-asp-net-mvc-rc2.aspx और कार्यों से लिया जाता है एएसपीनेट एमवीसी 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));
}

मैंने एमवीसी (विशेष रूप से एमवीसी 3 ) में 404 के सही तरीके से प्रबंधन करने के तरीके पर बहुत कम जांच की है, और यह, आईएमएचओ सबसे अच्छा समाधान है जिसके साथ मैं आया हूं:

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 अलग-अलग मामले हैं कि एएसपी.नेट एमवीसी 3 ऐप्स 404 उत्पन्न कर सकते हैं।

(स्वचालित रूप से एएसपी.नेट फ्रेमवर्क द्वारा उत्पन्न :)

(1) एक यूआरएल मार्ग तालिका में एक मैच नहीं मिलता है।

(स्वचालित रूप से एएसपी.नेट एमवीसी फ्रेमवर्क द्वारा उत्पन्न :)

(2) एक यूआरएल मार्ग तालिका में एक मैच पाता है, लेकिन एक अस्तित्व नियंत्रक निर्दिष्ट करता है।

(3) एक यूआरएल मार्ग तालिका में एक मैच पाता है, लेकिन एक अस्तित्वहीन कार्रवाई निर्दिष्ट करता है।

(मैन्युअल रूप से उत्पन्न :)

(4) एक क्रिया HttpNotFound () विधि का उपयोग करके एक HttpNotFoundResult देता है।

(5) एक कार्रवाई स्थिति कोड 404 के साथ एक HttpException फेंकता है।

(6) एक क्रिया मैन्युअल रूप से Response.StatusCode प्रॉपर्टी को 404 में संशोधित करती है।

आम तौर पर, आप 3 उद्देश्यों को पूरा करना चाहते हैं:

(1) उपयोगकर्ता को एक कस्टम 404 त्रुटि पेज दिखाएं।

(2) ग्राहक प्रतिक्रिया पर 404 स्टेटस कोड बनाए रखें (विशेष रूप से एसईओ के लिए महत्वपूर्ण)।

(3) 302 पुनर्निर्देशन के बिना सीधे प्रतिक्रिया भेजें।

इसे पूरा करने के प्रयास करने के कई तरीके हैं:

(1)

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

इस समाधान के साथ समस्याएं:

  1. मामलों (1), (4), (6) में उद्देश्य (1) का पालन नहीं करता है।
  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. केवल आईआईएस 7+ पर काम करता है।
  2. मामलों (2), (3), (5) में उद्देश्य (1) का पालन नहीं करता है।
  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. केवल आईआईएस 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. केवल आईआईएस 7+ पर काम करता है।
  2. उद्देश्य (2) स्वचालित रूप से पालन नहीं करता है। इसे मैन्युअल रूप से प्रोग्राम किया जाना चाहिए।
  3. मामलों (2), (3), (5) में उद्देश्य (3) का पालन नहीं करता है।

जो लोग इससे पहले परेशान हैं, उन्होंने अपनी खुद की पुस्तकालय बनाने की कोशिश की है ( http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html )। लेकिन पिछला समाधान बाहरी पुस्तकालय का उपयोग करने की जटिलता के बिना सभी मामलों को कवर करना प्रतीत होता है।


यहां एमवीसी टूल्स का उपयोग करने का एक और तरीका है जिसे आप बुरे कंट्रोलर नाम, खराब रूट नाम, और किसी भी अन्य मानदंड के अनुरोधों को संभाल सकते हैं जो आप एक्शन विधि के अंदर फिट बैठते हैं। व्यक्तिगत रूप से, मैं जितना संभव हो उतने web.config सेटिंग्स से बचना पसंद करता हूं, क्योंकि वे 302/200 रीडायरेक्ट करते हैं और रेज़र दृश्यों का उपयोग करते हुए ResponseRewrite ( Server.Transfer ) का समर्थन नहीं करते हैं। मैं एसईओ कारणों के लिए कस्टम त्रुटि पृष्ठ के साथ 404 वापस करना पसंद करूंगा।

इनमें से कुछ ऊपर cottsak की तकनीक पर नया ले लो है।

यह समाधान इसके बजाय एमवीसी 3 त्रुटि फ़िल्टर के पक्ष में न्यूनतम web.config सेटिंग्स का भी उपयोग करता है।

प्रयोग

बस एक कार्रवाई या कस्टम ActionFilterAttribute से एक HttpException फेंक दें।

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

चरण 1

अपने web.config पर निम्न सेटिंग जोड़ें। यह एमवीसी के हैंडलरर एट्रिब्यूट का उपयोग करने के लिए आवश्यक है।

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

चरण 2

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

Global.asax में GlobalFilterCollection ( GlobalFilters.Filters ) में फ़िल्टर जोड़ें। यह उदाहरण त्रुटि साझा दृश्य ( Views/Shared/Error.vbhtml . Views/Shared/Error.vbhtml ) में सभी InternalServerError (500) त्रुटियों को रूट करेगा। NotFound (404) त्रुटियों को साझा दृश्यों में ErrorHttp404.vbhtml पर भी भेजा जाएगा। मैंने आपको यह दिखाने के लिए यहां एक 401 त्रुटि जोड़ा है कि इसे अतिरिक्त HTTP त्रुटि कोड के लिए कैसे बढ़ाया जा सकता है। ध्यान दें कि इन्हें साझा दृश्य होना चाहिए, और वे सभी System.Web.Mvc.HandleErrorInfo रूप में 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

Create a base controller class and inherit from it in your controllers. This step allows us to handle unknown action names and raise the HTTP 404 error to our 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

Step 5

Create a ControllerFactory override, and override it in your Global.asax file in Application_Start. This step allows us to raise the HTTP 404 exception when an invalid controller name has been specified.

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)

Step 6

Include a special route in your RoutTable.Routes for the BaseController Unknown action. This will help us raise a 404 in the case where a user accesses an unknown controller, or unknown action.

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

सारांश

This example demonstrated how one can use the MVC framework to return 404 Http Error Codes to the browser without a redirect using filter attributes and shared error views. It also demonstrates showing the same custom error page when invalid controller names and action names are specified.

I'll add a screenshot of an invalid controller name, action name, and a custom 404 raised from the Home/TriggerNotFound action if I get enough votes to post one =). Fiddler returns a 404 message when I access the following URLs using this solution:

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

cottsak's post above and these articles were good references.


Adding my solution, which is almost identical to Herman Kan's, with a small wrinkle to allow it to work for my project.

Create a custom error controller:

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

Then create a custom controller factory:

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

Finally, add an override to the custom error controller:

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));
}

और बस। No need for Web.config changes.


Dealing with errors in ASP.NET MVC is just a pain in the butt. I tried a whole lot of suggestions on this page and on other questions and sites and nothing works good. One suggestion was to handle errors on web.config inside system.webserver but that just returns blank pages .

My goal when coming up with this solution was to;

  • NOT REDIRECT
  • Return PROPER STATUS CODES not 200/Ok like the default error handling

मेरा समाधान यहाँ है।

1 .Add the following to system.web section

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

The above handles any urls not handled by routes.config and unhandled exceptions especially those encountered on the views. Notice I used aspx not html . This is so I can add a response code on the code behind.

2 । Create a folder called Error (or whatever you prefer) at the root of your project and add the two webforms. Below is my 404 page;

<%@ 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>

And on the code behind I set the response code

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

Do the same for the 500 page

3 .To handle errors within the controllers. There's many ways to do it. इसी से मेरा काम बना है। All my controllers inherit from a base controller. In the base controller, I have the following methods

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 .Add the CustomError.cshtml to your Shared views folder. Below is mine;

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

Now in your application controller you can do something like this;

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();
    }
  }
}

Now for the caveat . It won't handle static file errors. So if you have a route such as example.com/widgets and the user changes it to example.com/widgets.html , they will get the IIS default error page so you have to handle IIS level errors some other way.


I went through most of the solutions posted on this thread. While this question might be old, it is still very applicable to new projects even now, so I spent quite a lot of time reading up on the answers presented here as well as else where.

As @Marco pointed out the different cases under which a 404 can happen, I checked the solution I compiled together against that list. In addition to his list of requirements, I also added one more.

  • The solution should be able to handle MVC as well as AJAX/WebAPI calls in the most appropriate manner. (ie if 404 happens in MVC, it should show the Not Found page and if 404 happens in WebAPI, it should not hijack the XML/JSON response so that the consuming Javascript can parse it easily).

This solution is 2 fold:

First part of it comes from @Guillaume at https://.com/a/27354140/2310818 . Their solution takes care of any 404 that were caused due to invalid route, invalid controller and invalid action.

The idea is to create a WebForm and then make it call the NotFound action of your MVC Errors Controller. It does all of this without any redirect so you will not see a single 302 in Fiddler. The original URL is also preserved, which makes this solution fantastic!

Second part of it comes from @Germán at https://.com/a/5536676/2310818 . Their solution takes care of any 404 returned by your actions in the form of HttpNotFoundResult() or throw new HttpException()!

The idea is to have a filter look at the response as well as the exception thrown by your MVC controllers and to call the appropriate action in your Errors Controller. Again this solution works without any redirect and the original url is preserved!

As you can see, both of these solutions together offer a very robust error handling mechanism and they achieve all the requirements listed by @Marco as well as my requirements. If you would like to see a working sample or a demo of this solution, please leave in the comments and I would be happy to put it together.


In MVC4 WebAPI 404 can be handle in the following way,

COURSES 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;
    }

HOME CONTROLLER

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

VIEW

<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 }
    );
}

परिणाम


My shortened solution that works with unhandled areas, controllers and actions:

  1. Create a view 404.cshtml.

  2. Create a base class for your controllers:

    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. Create a custom controller factory returning your base controller as a fallback:

    public class ControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType != null)
                return base.GetControllerInstance(requestContext, controllerType);
    
            return new Controller();
        }
    }
    
  4. Add to Application_Start() the following line:

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

My solution, in case someone finds it useful.

In Web.config:

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

In 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();
    }
}

Add a PageNotFound.cshtml in the Shared folder, and that's it.


Try NotFoundMVC on nuget. It works , no setup.







http-status-code-404