ASP.NET MVC कस्टम त्रुटि हैंडलिंग Application_Error Global.asax?


108

मेरे MVC एप्लिकेशन में त्रुटियों को निर्धारित करने के लिए मेरे पास कुछ बुनियादी कोड हैं। वर्तमान में अपने प्रोजेक्ट में मैं एक नियंत्रक कहा जाता है Errorकार्रवाई के तरीकों के साथ HTTPError404(), HTTPError500(), और General()। वे सभी एक स्ट्रिंग पैरामीटर स्वीकार करते हैं error। नीचे दिए गए कोड का उपयोग करना या संशोधित करना। प्रसंस्करण के लिए त्रुटि नियंत्रक के लिए डेटा पास करने का सबसे अच्छा / उचित तरीका क्या है? मैं चाहूंगा कि इसका समाधान यथासंभव मजबूत हो।

protected void Application_Error(object sender, EventArgs e)
{
    Exception exception = Server.GetLastError();
    Response.Clear();

    HttpException httpException = exception as HttpException;
    if (httpException != null)
    {
        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", "Error");
        switch (httpException.GetHttpCode())
        {
            case 404:
                // page not found
                routeData.Values.Add("action", "HttpError404");
                break;
            case 500:
                // server error
                routeData.Values.Add("action", "HttpError500");
                break;
            default:
                routeData.Values.Add("action", "General");
                break;
        }
        routeData.Values.Add("error", exception);
        // clear error on server
        Server.ClearError();

        // at this point how to properly pass route data to error controller?
    }
}

जवाबों:


104

इसके लिए एक नया मार्ग बनाने के बजाय, आप बस अपने नियंत्रक / कार्रवाई को पुनर्निर्देशित कर सकते हैं और सूचना को क्वेरिस्ट्रिंग के माध्यम से पास कर सकते हैं। उदाहरण के लिए:

protected void Application_Error(object sender, EventArgs e) {
  Exception exception = Server.GetLastError();
  Response.Clear();

  HttpException httpException = exception as HttpException;

  if (httpException != null) {
    string action;

    switch (httpException.GetHttpCode()) {
      case 404:
        // page not found
        action = "HttpError404";
        break;
      case 500:
        // server error
        action = "HttpError500";
        break;
      default:
        action = "General";
        break;
      }

      // clear error on server
      Server.ClearError();

      Response.Redirect(String.Format("~/Error/{0}/?message={1}", action, exception.Message));
    }

फिर आपका कंट्रोलर आपको जो चाहे प्राप्त करेगा:

// GET: /Error/HttpError404
public ActionResult HttpError404(string message) {
   return View("SomeView", message);
}

आपके दृष्टिकोण के साथ कुछ ट्रेडऑफ़ हैं। इस तरह की त्रुटि से निपटने में लूपिंग के साथ बहुत सावधान रहें। दूसरी बात यह है कि चूंकि आप 404 को हैंडल करने के लिए asp.net पाइपलाइन से गुजर रहे हैं, आप उन सभी हिट के लिए एक सत्र ऑब्जेक्ट बनाएंगे। यह भारी उपयोग की जाने वाली प्रणालियों के लिए एक मुद्दा (प्रदर्शन) हो सकता है।


जब आप कहते हैं "लूपिंग से सावधान रहें" तो आपका क्या मतलब है? क्या इस प्रकार की त्रुटि को पुनर्निर्देशित करने का एक बेहतर तरीका है (यह एक भारी उपयोग की गई प्रणाली थी)?
एरिक्रिक

4
लूपिंग से मेरा मतलब है कि जब आपको अपने त्रुटि पृष्ठ में कोई त्रुटि होती है, तो आपको बार-बार अपने त्रुटि पृष्ठ पर पुनर्निर्देशित किया जाएगा ... (उदाहरण के लिए, आप अपनी त्रुटि डेटाबेस में लॉग इन करना चाहते हैं और यह नीचे है)।
andrecarlucci

125
त्रुटियों पर पुनर्निर्देशन वेब की वास्तुकला के खिलाफ जाता है। जब सर्वर सही HTTP स्थिति कोड का जवाब देता है तो URI वही रहना चाहिए ताकि क्लाइंट को विफलता का सही संदर्भ पता हो। HandleErrorAttribute.OnException या Controller.OnException को लागू करना एक बेहतर समाधान है। और यदि वे विफल होते हैं, तो Global.asax में एक Server.Transfer ("~ / त्रुटि") करें।
असबजर्न उल्सबर्ग

1
@ क्रिस, यह स्वीकार्य है, लेकिन सबसे अच्छा अभ्यास नहीं है। विशेष रूप से चूंकि यह अक्सर HTTP 200 स्थिति कोड के साथ दी जाने वाली संसाधन फ़ाइल पर पुनर्निर्देशित होता है, जो क्लाइंट को यह विश्वास करने के लिए छोड़ देता है कि सब कुछ ठीक हो गया।
Asbjørn Ulsberg

1
मुझे सर्वर पर यह कार्य करने के लिए web में <httpErrors errorMode = "विस्तृत" /> जोड़ना था।
जीरो के के

28

प्रारंभिक प्रश्न का उत्तर देने के लिए "त्रुटि नियंत्रक के लिए राउडेट को ठीक से कैसे पास करें?"

IController errorController = new ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));

फिर अपने ErrorController वर्ग में, इस तरह एक समारोह को लागू:

[AcceptVerbs(HttpVerbs.Get)]
public ViewResult Error(Exception exception)
{
    return View("Error", exception);
}

यह अपवाद को दृश्य में धकेलता है। दृश्य पृष्ठ इस प्रकार घोषित किया जाना चाहिए:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<System.Exception>" %>

और त्रुटि प्रदर्शित करने के लिए कोड:

<% if(Model != null) { %>  <p><b>Detailed error:</b><br />  <span class="error"><%= Helpers.General.GetErrorMessage((Exception)Model, false) %></span></p> <% } %>

यहां वह कार्य है जो अपवाद पेड़ से सभी अपवाद संदेशों को इकट्ठा करता है:

    public static string GetErrorMessage(Exception ex, bool includeStackTrace)
    {
        StringBuilder msg = new StringBuilder();
        BuildErrorMessage(ex, ref msg);
        if (includeStackTrace)
        {
            msg.Append("\n");
            msg.Append(ex.StackTrace);
        }
        return msg.ToString();
    }

    private static void BuildErrorMessage(Exception ex, ref StringBuilder msg)
    {
        if (ex != null)
        {
            msg.Append(ex.Message);
            msg.Append("\n");
            if (ex.InnerException != null)
            {
                BuildErrorMessage(ex.InnerException, ref msg);
            }
        }
    }

9

मैंने Lion_cl द्वारा नोट किए गए ajax समस्या का समाधान पाया।

Global.asax:

protected void Application_Error()
    {           
        if (HttpContext.Current.Request.IsAjaxRequest())
        {
            HttpContext ctx = HttpContext.Current;
            ctx.Response.Clear();
            RequestContext rc = ((MvcHandler)ctx.CurrentHandler).RequestContext;
            rc.RouteData.Values["action"] = "AjaxGlobalError";

            // TODO: distinguish between 404 and other errors if needed
            rc.RouteData.Values["newActionName"] = "WrongRequest";

            rc.RouteData.Values["controller"] = "ErrorPages";
            IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
            IController controller = factory.CreateController(rc, "ErrorPages");
            controller.Execute(rc);
            ctx.Server.ClearError();
        }
    }

ErrorPagesController

public ActionResult AjaxGlobalError(string newActionName)
    {
        return new AjaxRedirectResult(Url.Action(newActionName), this.ControllerContext);
    }

AjaxRedirectResult

public class AjaxRedirectResult : RedirectResult
{
    public AjaxRedirectResult(string url, ControllerContext controllerContext)
        : base(url)
    {
        ExecuteResult(controllerContext);
    }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context.RequestContext.HttpContext.Request.IsAjaxRequest())
        {
            JavaScriptResult result = new JavaScriptResult()
            {
                Script = "try{history.pushState(null,null,window.location.href);}catch(err){}window.location.replace('" + UrlHelper.GenerateContentUrl(this.Url, context.HttpContext) + "');"
            };

            result.ExecuteResult(context);
        }
        else
        {
            base.ExecuteResult(context);
        }
    }
}

AjaxRequestExtension

public static class AjaxRequestExtension
{
    public static bool IsAjaxRequest(this HttpRequest request)
    {
        return (request.Headers["X-Requested-With"] != null && request.Headers["X-Requested-With"] == "XMLHttpRequest");
    }
}

इसे लागू करते समय मुझे निम्नलिखित त्रुटि मिली: 'System.Web.HttpRequest' में 'IsAjaxRequest' की परिभाषा नहीं है। इस लेख का एक हल है: stackoverflow.com/questions/14629304/…
जूलियन डोरमन

8

मैं इससे पहले MVC ऐप में एक वैश्विक त्रुटि से निपटने की दिनचर्या को केंद्रीकृत करने के विचार से जूझता था। मैं ASP.NET मंचों पर एक पोस्ट है

यह मूल रूप से एक त्रुटि नियंत्रक की आवश्यकता के बिना Global.asax में आपके सभी एप्लिकेशन त्रुटियों को संभालता है, [HandlerError]विशेषता के साथ सजाता है, या customErrorsweb.config में नोड के साथ फ़िडलिंग करता है।


6

एमवीसी में त्रुटियों को संभालने का एक बेहतर तरीका शायद अपने नियंत्रक या कार्य के लिए हैंडलेयर विशेषता को लागू करना है और जो आप चाहते हैं उसे करने के लिए साझा / त्रुटि .aspx फ़ाइल को अपडेट करें। उस पृष्ठ पर मॉडल ऑब्जेक्ट में अपवाद संपत्ति के साथ-साथ कंट्रोलरनाम और एक्शननाम भी शामिल है।


1
404फिर आप किसी त्रुटि को कैसे संभालेंगे ? चूंकि इसके लिए कोई नियंत्रक / कार्रवाई निर्दिष्ट नहीं है?
9

स्वीकृत उत्तर में 404 शामिल हैं। यह दृष्टिकोण केवल 500 त्रुटियों के लिए उपयोगी है।
ब्रायन

हो सकता है कि आप अपने जवाब में इसे संपादित करें। Perhaps a better way of handling errorsसभी त्रुटियों की तरह बहुत ज्यादा लगता है और केवल 500 नहीं।
अद्र्धवार्षिक

4

Application_Error अजाक्स अनुरोध के साथ समस्या है। यदि त्रुटि को एक्शन में नियंत्रित किया जाता है जिसे अजाक्स द्वारा बुलाया जाता है - यह परिणामी कंटेनर के अंदर आपकी त्रुटि दृश्य प्रदर्शित करेगा।


4

यह MVC ( https://stackoverflow.com/a/9461386/5869805 ) का सबसे अच्छा तरीका नहीं हो सकता है

नीचे आप कैसे Application_Error में एक दृश्य प्रस्तुत करते हैं और इसे http प्रतिक्रिया के लिए लिखते हैं। आपको रीडायरेक्ट का उपयोग करने की आवश्यकता नहीं है। यह सर्वर के लिए एक दूसरे अनुरोध को रोक देगा, इसलिए ब्राउज़र के एड्रेस बार में लिंक समान रहेगा। यह अच्छा या बुरा हो सकता है, यह इस बात पर निर्भर करता है कि आप क्या चाहते हैं।

Global.asax.cs

protected void Application_Error()
{
    var exception = Server.GetLastError();
    // TODO do whatever you want with exception, such as logging, set errorMessage, etc.
    var errorMessage = "SOME FRIENDLY MESSAGE";

    // TODO: UPDATE BELOW FOUR PARAMETERS ACCORDING TO YOUR ERROR HANDLING ACTION
    var errorArea = "AREA";
    var errorController = "CONTROLLER";
    var errorAction = "ACTION";
    var pathToViewFile = $"~/Areas/{errorArea}/Views/{errorController}/{errorAction}.cshtml"; // THIS SHOULD BE THE PATH IN FILESYSTEM RELATIVE TO WHERE YOUR CSPROJ FILE IS!

    var requestControllerName = Convert.ToString(HttpContext.Current.Request.RequestContext?.RouteData?.Values["controller"]);
    var requestActionName = Convert.ToString(HttpContext.Current.Request.RequestContext?.RouteData?.Values["action"]);

    var controller = new BaseController(); // REPLACE THIS WITH YOUR BASE CONTROLLER CLASS
    var routeData = new RouteData { DataTokens = { { "area", errorArea } }, Values = { { "controller", errorController }, {"action", errorAction} } };
    var controllerContext = new ControllerContext(new HttpContextWrapper(HttpContext.Current), routeData, controller);
    controller.ControllerContext = controllerContext;

    var sw = new StringWriter();
    var razorView = new RazorView(controller.ControllerContext, pathToViewFile, "", false, null);
    var model = new ViewDataDictionary(new HandleErrorInfo(exception, requestControllerName, requestActionName));
    var viewContext = new ViewContext(controller.ControllerContext, razorView, model, new TempDataDictionary(), sw);
    viewContext.ViewBag.ErrorMessage = errorMessage;
    //TODO: add to ViewBag what you need
    razorView.Render(viewContext, sw);
    HttpContext.Current.Response.Write(sw);
    Server.ClearError();
    HttpContext.Current.Response.End(); // No more processing needed (ex: by default controller/action routing), flush the response out and raise EndRequest event.
}

राय

@model HandleErrorInfo
@{
    ViewBag.Title = "Error";
    // TODO: SET YOUR LAYOUT
}
<div class="">
    ViewBag.ErrorMessage
</div>
@if(Model != null && HttpContext.Current.IsDebuggingEnabled)
{
    <div class="" style="background:khaki">
        <p>
            <b>Exception:</b> @Model.Exception.Message <br/>
            <b>Controller:</b> @Model.ControllerName <br/>
            <b>Action:</b> @Model.ActionName <br/>
        </p>
        <div>
            <pre>
                @Model.Exception.StackTrace
            </pre>
        </div>
    </div>
}

यह IMO का सबसे अच्छा तरीका है। ठीक वही जो मेरे द्वारा खोजा जा रहा था।
स्टीव हैरिस

@SteveHarris को इससे मदद मिली! :)
बर्काय

3

ब्रायन, यह दृष्टिकोण गैर-अजाक्स अनुरोधों के लिए बहुत अच्छा काम करता है, लेकिन जैसा कि Lion_cl ने कहा है, यदि आपके पास अजाक्स कॉल के दौरान कोई त्रुटि है, तो आपका Share / Error.aspx दृश्य (या आपका कस्टम त्रुटि पृष्ठ दृश्य) Ajax कॉलर पर वापस आ जाएगा- -उपयोगकर्ता को त्रुटि पृष्ठ पर पुनर्निर्देशित नहीं किया जाएगा।


0

रूट पेज पर रीडायरेक्ट करने के लिए निम्नलिखित कोड का उपयोग करें। अपवाद का उपयोग करें। Coz अपवाद क्वेरी स्ट्रिंग त्रुटि देता है अगर यह querystring लंबाई का विस्तार करता है।

routeData.Values.Add("error", exception.Message);
// clear error on server
Server.ClearError();
Response.RedirectToRoute(routeData.Values);

-1

मुझे इस त्रुटि से निपटने के दृष्टिकोण की समस्या है: web.config के मामले में:

<customErrors mode="On"/>

त्रुटि हैंडलर खोज त्रुटि है। Html और नियंत्रण प्रवाह कदम Application_Error Global.asax में अपवाद के बाद ही

System.InvalidOperationException: दृश्य 'त्रुटि' या इसके स्वामी को नहीं मिला था या कोई दृश्य इंजन खोजे गए स्थानों का समर्थन नहीं करता है। निम्नलिखित स्थानों को खोजा गया: ~ / Views / home / Error.aspx ~ / Views / home / Error.ascx ~ / Views / साझा / Error.aspx ~ / Views / साझा / त्रुटि.ascx ~ / Views / home / त्रुटि। cshtml ~ / Views / home / Error.vbhtml ~ / Views / Shared / Error.cshtml ~ / Views / Shared / Error.vbhtml at System.Web.Mvc.ViewResult.FindView (कंट्रोलर कॉन्टेक्स्ट संदर्भ) ........ ............

इसलिए

 Exception exception = Server.GetLastError();
  Response.Clear();
  HttpException httpException = exception as HttpException;

httpException हमेशा null होती है फिर customErrors mode = "On" :( यह भ्रामक है <customErrors mode="Off"/>या फिर <customErrors mode="RemoteOnly"/>उपयोगकर्ताओं को customErrors html, फिर customErrors मोड = "ऑन" दिखाई देता है यह कोड भी गलत है


इस कोड की एक और समस्या है कि

Response.Redirect(String.Format("~/Error/{0}/?message={1}", action, exception.Message));

वास्तविक त्रुटि कोड (402,403 आदि) के बजाय कोड 302 के साथ पृष्ठ लौटाएं

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.