C# ASP.NET MVC 自定义错误处理 Application_Error Global.asax?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1171035/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ASP.NET MVC Custom Error Handling Application_Error Global.asax?
提问by aherrick
I have some basic code to determine errors in my MVC application. Currently in my project I have a controller called Error
with action methods HTTPError404()
, HTTPError500()
, and General()
. They all accept a string parameter error
. Using or modifying the code below.
What is the best/proper way to pass the data to the Error controller for processing? I would like to have a solution as robust as possible.
我有一些基本的代码来确定我的 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?
}
}
采纳答案by andrecarlucci
Instead of creating a new route for that, you could just redirect to your controller/action and pass the information via querystring. For instance:
您无需为此创建新路由,而只需重定向到您的控制器/操作并通过查询字符串传递信息。例如:
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));
}
Then your controller will receive whatever you want:
然后您的控制器将收到您想要的任何内容:
// GET: /Error/HttpError404
public ActionResult HttpError404(string message) {
return View("SomeView", message);
}
There are some tradeoffs with your approach. Be very very careful with looping in this kind of error handling. Other thing is that since you are going through the asp.net pipeline to handle a 404, you will create a session object for all those hits. This can be an issue (performance) for heavily used systems.
你的方法有一些权衡。在这种错误处理中循环时要非常小心。另一件事是,由于您正在通过 asp.net 管道来处理 404,您将为所有这些命中创建一个会话对象。对于频繁使用的系统来说,这可能是一个问题(性能)。
回答by Brian
Perhaps a better way of handling errors in MVC is to apply the HandleError attribute to your controller or action and update the Shared/Error.aspx file to do what you want. The Model object on that page includes an Exception property as well as ControllerName and ActionName.
也许在 MVC 中处理错误的更好方法是将 HandleError 属性应用于您的控制器或操作并更新 Shared/Error.aspx 文件以执行您想要的操作。该页面上的 Model 对象包括一个 Exception 属性以及 ControllerName 和 ActionName。
回答by Victor Gelmutdinov
Application_Error having issue with Ajax requests. If error handled in Action which called by Ajax - it will display your Error View inside the resulting container.
Application_Error 有 Ajax 请求的问题。如果在 Ajax 调用的 Action 中处理错误 - 它将在结果容器中显示您的错误视图。
回答by Victor Gelmutdinov
To answer the initial question "how to properly pass routedata to error controller?":
回答最初的问题“如何正确地将路由数据传递给错误控制器?”:
IController errorController = new ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
Then in your ErrorController class, implement a function like this:
然后在你的 ErrorController 类中,实现一个像这样的函数:
[AcceptVerbs(HttpVerbs.Get)]
public ViewResult Error(Exception exception)
{
return View("Error", exception);
}
This pushes the exception into the View. The view page should be declared as follows:
这会将异常推送到视图中。视图页面应声明如下:
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<System.Exception>" %>
And the code to display the error:
以及显示错误的代码:
<% if(Model != null) { %> <p><b>Detailed error:</b><br /> <span class="error"><%= Helpers.General.GetErrorMessage((Exception)Model, false) %></span></p> <% } %>
Here is the function that gathers the all exception messages from the exception tree:
这是从异常树中收集所有异常消息的函数:
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);
}
}
}
回答by Hyman Hsu
I struggled with the idea of centralizing a global error handling routine in an MVC app before. I have a post on the ASP.NET forums.
以前,我对在 MVC 应用程序中集中全局错误处理例程的想法感到困惑。我在 ASP.NET 论坛上发表了一篇文章。
It basically handles all your application errors in the global.asax without the need for an error controller, decorating with the [HandlerError]
attribute, or fiddling with the customErrors
node in the web.config.
它基本上可以处理 global.asax 中的所有应用程序错误,而无需错误控制器、用[HandlerError]
属性装饰或摆弄customErrors
web.config 中的节点。
回答by Jozef Krchňavy
I found a solution for ajax issue noted by Lion_cl.
我找到了 Lion_cl 指出的 ajax 问题的解决方案。
global.asax:
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
Ajax 重定向结果
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
Ajax 请求扩展
public static class AjaxRequestExtension
{
public static bool IsAjaxRequest(this HttpRequest request)
{
return (request.Headers["X-Requested-With"] != null && request.Headers["X-Requested-With"] == "XMLHttpRequest");
}
}
回答by undeniablyrob
Brian, This approach works great for non-Ajax requests, but as Lion_cl stated, if you have an error during an Ajax call, your Share/Error.aspx view (or your custom error page view) will be returned to the Ajax caller--the user will NOT be redirected to the error page.
Brian,这种方法非常适用于非 Ajax 请求,但正如 Lion_cl 所说,如果您在 Ajax 调用期间出错,您的 Share/Error.aspx 视图(或您的自定义错误页面视图)将返回给 Ajax 调用方- - 用户不会被重定向到错误页面。
回答by Swapnil Malap
Use Following code for redirecting on route page. Use exception.Message instide of exception. Coz exception query string gives error if it extends the querystring length.
使用以下代码在路由页面上重定向。在异常内使用 exception.Message。Coz 异常查询字符串如果扩展了查询字符串长度,则会出错。
routeData.Values.Add("error", exception.Message);
// clear error on server
Server.ClearError();
Response.RedirectToRoute(routeData.Values);
回答by burkay
This may not be the best way for MVC ( https://stackoverflow.com/a/9461386/5869805)
这可能不是 MVC 的最佳方式(https://stackoverflow.com/a/9461386/5869805)
Below is how you render a view in Application_Error and write it to http response. You do not need to use redirect. This will prevent a second request to server, so the link in browser's address bar will stay same. This may be good or bad, it depends on what you want.
以下是在 Application_Error 中呈现视图并将其写入 http 响应的方法。您不需要使用重定向。这将阻止对服务器的第二次请求,因此浏览器地址栏中的链接将保持不变。这可能是好是坏,这取决于您想要什么。
Global.asax.cs
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.
}
View
看法
@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>
}
回答by Александр Шмыков
I have problem with this error handling approach: In case of web.config:
我对这种错误处理方法有疑问:如果是 web.config:
<customErrors mode="On"/>
The error handler is searching view Error.shtml and the control flow step in to Application_Error global.asax only after exception
错误处理程序正在搜索视图 Error.shtml 并且控制流仅在异常之后才进入 Application_Error global.asax
System.InvalidOperationException: The view 'Error' or its master was not found or no view engine supports the searched locations. The following locations were searched: ~/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 at System.Web.Mvc.ViewResult.FindView(ControllerContext context) ....................
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 context) ........ ………………
So
所以
Exception exception = Server.GetLastError();
Response.Clear();
HttpException httpException = exception as HttpException;
httpException is always null then
customErrors mode="On"
:(
It is misleading
Then <customErrors mode="Off"/>
or <customErrors mode="RemoteOnly"/>
the users see customErrors html,
Then customErrors mode="On" this code is wrong too
httpException 总是 null then customErrors mode="On" :( 这是误导 然后<customErrors mode="Off"/>
或者<customErrors mode="RemoteOnly"/>
用户看到 customErrors html, Then customErrors mode="On" 这段代码也是错误的
Another problem of this code that
这段代码的另一个问题是
Response.Redirect(String.Format("~/Error/{0}/?message={1}", action, exception.Message));
Return page with code 302 instead real error code(402,403 etc)
返回带有代码 302 的页面而不是真正的错误代码(402,403 等)