![](/img/trans.png)
[英]Should you use the method Application_Error in your global.asax in ASP.NET MVC?
[英]ASP.NET MVC Custom Error Handling Application_Error Global.asax?
我有一些基本代碼來確定我的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?
}
}
無需為此創建新路由,您只需重定向到控制器/操作並通過querystring傳遞信息即可。 例如:
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);
}
您的方法需要權衡取舍。 在這種錯誤處理中循環時要非常小心。 另一件事是,由於要通過asp.net管道來處理404,因此將為所有這些匹配創建會話對象。 對於頻繁使用的系統,這可能是一個問題(性能)。
要回答最初的問題“如何正確地將routedata傳遞給錯誤控制器?”:
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);
}
}
}
之前,我不敢在MVC應用程序中集中全局錯誤處理例程的想法。 我在ASP.NET論壇上有一篇文章 。
它基本上可以處理global.asax中的所有應用程序錯誤,而無需錯誤控制器,使用[HandlerError]
屬性進行[HandlerError]
或使用web.config中的customErrors
節點。
我找到了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");
}
}
在MVC中處理錯誤的更好方法可能是將HandleError屬性應用於控制器或操作,並更新Shared / Error.aspx文件以執行所需的操作。 該頁面上的Model對象包括Exception屬性以及ControllerName和ActionName。
Application_Error與Ajax請求有關。 如果在由Ajax調用的Action中處理了錯誤,它將在結果容器內顯示您的錯誤視圖。
這可能不是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>
}
Brian,這種方法對非Ajax請求非常有效,但是正如Lion_cl所述,如果在Ajax調用過程中出錯,則您的Share / Error.aspx視圖(或自定義錯誤頁面視圖)將返回給Ajax調用者, -用戶將不會被重定向到錯誤頁面。
使用以下代碼在路線頁面上進行重定向。 使用exception.Message實例化異常。 如果Coz異常查詢字符串擴展了查詢字符串的長度,則會給出錯誤。
routeData.Values.Add("error", exception.Message);
// clear error on server
Server.ClearError();
Response.RedirectToRoute(routeData.Values);
我對這種錯誤處理方法有疑問:對於web.config:
<customErrors mode="On"/>
錯誤處理程序正在搜索View Error.shtml,並且僅在異常發生后,控制流才進入Application_Error global.asax。
System.InvalidOperationException:找不到視圖“錯誤”或其主視圖,或者沒有視圖引擎支持搜索到的位置。 搜索以下位置:〜/ Views / home / Error.aspx〜/ Views / home / Error.ascx〜/ Views / Shared / Error.aspx〜/ Views / Shared / Error.ascx〜/ Views / home / Error。 cshtml〜/ Views / home / Error.vbhtml〜/ Views / Shared / Error.cshtml〜/ Views / Shared / Error.vbhtml位於System.Web.Mvc.ViewResult.FindView(ControllerContext上下文)........ ............
所以
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 mode =“ On”這樣代碼也是錯誤的
該代碼的另一個問題是
Response.Redirect(String.Format("~/Error/{0}/?message={1}", action, exception.Message));
返回頁面,代碼為302,而不是實際錯誤代碼(402,403等)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.