ASP.NET MVC Custom Error Handling Application Error Global.асакс?
у меня есть базовый код для определения ошибок в моем приложении 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?
}
}
10 ответов:
вместо того, чтобы создавать новый маршрут для этого, вы можете просто перенаправить на свой контроллер/действие и передать информацию через строку запроса. Например:
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); } } }
Я нашел решение для проблемы ajax, отмеченной Lion_cl.
глобальные.асакс:
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 раньше. У меня есть сообщение на ASP.NET форумы.
он в основном обрабатывает все ваши ошибки приложений в глобальном масштабе.asax без необходимости в контроллере ошибок, украшая с
[HandlerError]атрибут, или возиться сcustomErrorsузел в интернете.конфиг.
возможно, лучшим способом обработки ошибок в MVC является применение атрибута HandleError к вашему контроллеру или действию и обновление Shared/Error.aspx файл, чтобы сделать то, что вы хотите. Объект модели на этой странице содержит свойство Exception, а также ControllerName и ActionName.
Application_Error возникли проблемы с Ajax-запросами. Если ошибка обрабатывается в действии, которое вызывается Ajax-он будет отображать ваше представление ошибки внутри результирующего контейнера.
Брайан, Этот подход отлично подходит для запросов, отличных от Ajax, но, как заявил Lion_cl, если у вас есть ошибка во время вызова Ajax, ваша доля/ошибка.представление aspx (или ваше пользовательское представление страницы ошибок) будет возвращено вызывающему Ajax-пользователь не будет перенаправлен на страницу ошибок.
это может быть не лучший способ для MVC ( https://stackoverflow.com/a/9461386/5869805)
ниже показано, как вы отображаете представление в Application_Error и записываете его в http-ответ. Вам не нужно использовать редирект. Это предотвратит второй запрос к серверу, поэтому ссылка в адресной строке браузера останется прежней. Это может быть хорошо или плохо, это зависит от того, что вы хотеть.
глобальные.асакс.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> }
используйте следующий код для перенаправления на странице маршрута. Используйте исключение.Сообщение instide исключения. Строка запроса исключения Coz дает ошибку, если она расширяет длину строки запроса.
routeData.Values.Add("error", exception.Message); // clear error on server Server.ClearError(); Response.RedirectToRoute(routeData.Values);
у меня проблема с этим подходом обработки ошибок: В случае паутины.config:
<customErrors mode="On"/>обработчик ошибок ищет ошибку представления.shtml и шаг потока управления в Application_Error global.asax только после исключения
Comments