Как создать пользовательские страницы ошибок в ASP.NET MVC 4
Я хочу создать страницу пользовательских ошибок для 500, 404 и 403. Вот что я сделал:
-
Включить пользовательские ошибки в файле web.config следующим образом:
<customErrors mode="On"
defaultRedirect="~/Views/Shared/Error.cshtml">
<error statusCode="403"
redirect="~/Views/Shared/UnauthorizedAccess.cshtml" />
<error statusCode="404"
redirect="~/Views/Shared/FileNotFound.cshtml" />
</customErrors>
-
Зарегистрировано HandleErrorAttribute
как глобальный фильтр действий в классе FilterConfig
следующим образом:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomHandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
}
-
Создал пользовательскую страницу ошибок для каждого из приведенных выше сообщений. Значение по умолчанию для 500 уже было доступно из коробки.
-
Объявлено в каждом пользовательском представлении страницы ошибки, что модель для страницы System.Web.Mvc.HandleErrorInfo
В течение 500 страниц отображается страница пользовательской ошибки. Для других это не так.
Есть ли что-то, что мне не хватает?
Похоже, что это не все, что нужно для отображения пользовательских ошибок, когда я читаю код в методе OnException
класса HandleErrorAttribute
, и он обрабатывает только 500.
Что мне нужно сделать для обработки других ошибок?
Ответы
Ответ 1
Моя текущая настройка (на MVC3, но я думаю, что она по-прежнему применяется) полагается на наличие ErrorController
, поэтому я использую:
<system.web>
<customErrors mode="On" defaultRedirect="~/Error">
<error redirect="~/Error/NotFound" statusCode="404" />
</customErrors>
</system.web>
И контроллер содержит следующее:
public class ErrorController : Controller
{
public ViewResult Index()
{
return View("Error");
}
public ViewResult NotFound()
{
Response.StatusCode = 404; //you may want to set this to 200
return View("NotFound");
}
}
И взгляды так же, как вы их реализуете. Я, как правило, добавляю немного логики, чтобы показать трассировку стека и информацию об ошибках, если приложение находится в режиме отладки. Итак, Error.cshtml выглядит примерно так:
@model System.Web.Mvc.HandleErrorInfo
@{
Layout = "_Layout.cshtml";
ViewBag.Title = "Error";
}
<div class="list-header clearfix">
<span>Error</span>
</div>
<div class="list-sfs-holder">
<div class="alert alert-error">
An unexpected error has occurred. Please contact the system administrator.
</div>
@if (Model != null && HttpContext.Current.IsDebuggingEnabled)
{
<div>
<p>
<b>Exception:</b> @Model.Exception.Message<br />
<b>Controller:</b> @Model.ControllerName<br />
<b>Action:</b> @Model.ActionName
</p>
<div style="overflow:scroll">
<pre>
@Model.Exception.StackTrace
</pre>
</div>
</div>
}
</div>
Ответ 2
Я сделал решение pablo, и у меня всегда была ошибка (MVC4)
Вид "Ошибка" или его мастер не найден или механизм просмотра не поддерживает найденное местоположение.
Чтобы избавиться от этого, удалите строку
filters.Add(new HandleErrorAttribute());
в FilterConfig.cs
Ответ 3
Я делаю то, что требует меньше кодирования, чем другие опубликованные решения.
Во-первых, в моем web.config у меня есть следующее:
<customErrors mode="On" defaultRedirect="~/ErrorPage/Oops">
<error redirect="~/ErrorPage/Oops/404" statusCode="404" />
<error redirect="~/ErrorPage/Oops/500" statusCode="500" />
</customErrors>
И контроллер (/Controllers/ErrorPageController.cs) содержит следующее:
public class ErrorPageController : Controller
{
public ActionResult Oops(int id)
{
Response.StatusCode = id;
return View();
}
}
И, наконец, представление содержит следующее (урезанное для простоты, но оно может быть conta:
@{ ViewBag.Title = "Oops! Error Encountered"; }
<section id="Page">
<div class="col-xs-12 well">
<table cellspacing="5" cellpadding="3" style="background-color:#fff;width:100%;" class="table-responsive">
<tbody>
<tr>
<td valign="top" align="left" id="tableProps">
<img width="25" height="33" src="~/Images/PageError.gif" id="pagerrorImg">
</td>
<td width="360" valign="middle" align="left" id="tableProps2">
<h1 style="COLOR: black; FONT: 13pt/15pt verdana" id="errortype"><span id="errorText">@Response.Status</span></h1>
</td>
</tr>
<tr>
<td width="400" colspan="2" id="tablePropsWidth"><font style="COLOR: black; FONT: 8pt/11pt verdana">Possible causes:</font>
</td>
</tr>
<tr>
<td width="400" colspan="2" id="tablePropsWidth2">
<font style="COLOR: black; FONT: 8pt/11pt verdana" id="LID1">
<hr>
<ul>
<li id="list1">
<span class="infotext">
<strong>Baptist explanation: </strong>There
must be sin in your life. Everyone else opened it fine.<br>
</span>
</li>
<li>
<span class="infotext">
<strong>Presbyterian explanation: </strong>It's
not God will for you to open this link.<br>
</span>
</li>
<li>
<span class="infotext">
<strong> Word of Faith explanation:</strong>
You lack the faith to open this link. Your negative words have prevented
you from realizing this link fulfillment.<br>
</span>
</li>
<li>
<span class="infotext">
<strong>Charismatic explanation: </strong>Thou
art loosed! Be commanded to OPEN!<br>
</span>
</li>
<li>
<span class="infotext">
<strong>Unitarian explanation:</strong> All
links are equal, so if this link doesn't work for you, feel free to
experiment with other links that might bring you joy and fulfillment.<br>
</span>
</li>
<li>
<span class="infotext">
<strong>Buddhist explanation:</strong> .........................<br>
</span>
</li>
<li>
<span class="infotext">
<strong>Episcopalian explanation:</strong>
Are you saying you have something against homosexuals?<br>
</span>
</li>
<li>
<span class="infotext">
<strong>Christian Science explanation: </strong>There
really is no link.<br>
</span>
</li>
<li>
<span class="infotext">
<strong>Atheist explanation: </strong>The only
reason you think this link exists is because you needed to invent it.<br>
</span>
</li>
<li>
<span class="infotext">
<strong>Church counselor explanation:</strong>
And what did you feel when the link would not open?
</span>
</li>
</ul>
<p>
<br>
</p>
<h2 style="font:8pt/11pt verdana; color:black" id="ietext">
<img width="16" height="16" align="top" src="~/Images/Search.gif">
HTTP @Response.StatusCode - @Response.StatusDescription <br>
</h2>
</font>
</td>
</tr>
</tbody>
</table>
</div>
</section>
Ответ 4
Я бы рекомендовал использовать файл Global.asax.cs.
protected void Application_Error(Object sender, EventArgs e)
{
var exception = Server.GetLastError();
if (exception is HttpUnhandledException)
{
Server.Transfer("~/Error.aspx");
}
if (exception != null)
{
Server.Transfer("~/Error.aspx");
}
try
{
// This is to stop a problem where we were seeing "gibberish" in the
// chrome and firefox browsers
HttpApplication app = sender as HttpApplication;
app.Response.Filter = null;
}
catch
{
}
}
Ответ 5
Кажется, здесь несколько шагов смешались. Я буду предлагать то, что я сделал с нуля.
-
Создайте контроллер ErrorPage
public class ErrorPageController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Oops(int id)
{
Response.StatusCode = id;
return View();
}
}
-
Добавить виды для этих двух действий (щелкните правой кнопкой мыши → Добавить вид). Они должны появиться в папке с именем ErrorPage.
-
Внутри App_Start
откройте FilterConfig.cs
и закомментируйте фильтр обработки ошибок.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
// Remove this filter because we want to handle errors ourselves via the ErrorPage controller
//filters.Add(new HandleErrorAttribute());
}
-
Внутри web.config добавьте следующие записи <customerErrors>
в разделе System.Web
<customErrors mode="On" defaultRedirect="~/ErrorPage/Oops">
<error redirect="~/ErrorPage/Oops/404" statusCode="404" />
<error redirect="~/ErrorPage/Oops/500" statusCode="500" />
</customErrors>
-
Тест (конечно). Бросьте необработанное исключение в свой код и посмотрите, как он перейдет на страницу с идентификатором 500, а затем используйте URL-адрес для страницы, которой не существует, чтобы увидеть 404.
Спасибо всем выше. Соответственно поддерживается.
Ответ 6
Основываясь на ответе, опубликованном maxspan, я собрал минимальный образец проекта на GitHub, показывающий все рабочие части.
В принципе, мы просто добавляем метод Application_Error
к global.asax.cs, чтобы перехватить исключение и дать нам возможность перенаправить (или, вернее, запрос на передачу) на страницу пользовательских ошибок.
protected void Application_Error(Object sender, EventArgs e)
{
// See http://stackoverflow.com/questions/13905164/how-to-make-custom-error-pages-work-in-asp-net-mvc-4
// for additional context on use of this technique
var exception = Server.GetLastError();
if (exception != null)
{
// This would be a good place to log any relevant details about the exception.
// Since we are going to pass exception information to our error page via querystring,
// it will only be practical to issue a short message. Further detail would have to be logged somewhere.
// This will invoke our error page, passing the exception message via querystring parameter
// Note that we chose to use Server.TransferRequest, which is only supported in IIS 7 and above.
// As an alternative, Response.Redirect could be used instead.
// Server.Transfer does not work (see https://support.microsoft.com/en-us/kb/320439 )
Server.TransferRequest("~/Error?Message=" + exception.Message);
}
}
Контроллер ошибок:
/// <summary>
/// This controller exists to provide the error page
/// </summary>
public class ErrorController : Controller
{
/// <summary>
/// This action represents the error page
/// </summary>
/// <param name="Message">Error message to be displayed (provided via querystring parameter - a design choice)</param>
/// <returns></returns>
public ActionResult Index(string Message)
{
// We choose to use the ViewBag to communicate the error message to the view
ViewBag.Message = Message;
return View();
}
}
Страница ошибки:
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
</head>
<body>
<h2>My Error</h2>
<p>@ViewBag.Message</p>
</body>
</html>
Ничего другого не участвует, кроме отключения/удаления filters.Add(new HandleErrorAttribute())
в FilterConfig.cs
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//filters.Add(new HandleErrorAttribute()); // <== disable/remove
}
}
В то время как очень простой в реализации, единственный недостаток, который я вижу в этом подходе, заключается в использовании запроса для предоставления информации об исключении на странице целевой ошибки.
Ответ 7
Вот мое решение. Использовать [ExportModelStateToTempData]/[ImportModelStateFromTempData] неудобно, на мой взгляд.
~/Views/Home/Error.cshtml:
@{
ViewBag.Title = "Error";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Error</h2>
<hr/>
<div style="min-height: 400px;">
@Html.ValidationMessage("Error")
<br />
<br />
<button onclick="Error_goBack()" class="k-button">Go Back</button>
<script>
function Error_goBack() {
window.history.back()
}
</script>
</div>
~/Контроллеры/HomeController.sc:
public class HomeController : BaseController
{
public ActionResult Index()
{
return View();
}
public ActionResult Error()
{
return this.View();
}
...
}
~/Контроллеры/BaseController.sc:
public class BaseController : Controller
{
public BaseController() { }
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Result is ViewResult)
{
if (filterContext.Controller.TempData.ContainsKey("Error"))
{
var modelState = filterContext.Controller.TempData["Error"] as ModelState;
filterContext.Controller.ViewData.ModelState.Merge(new ModelStateDictionary() { new KeyValuePair<string, ModelState>("Error", modelState) });
filterContext.Controller.TempData.Remove("Error");
}
}
if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult))
{
if (filterContext.Controller.ViewData.ModelState.ContainsKey("Error"))
{
filterContext.Controller.TempData["Error"] = filterContext.Controller.ViewData.ModelState["Error"];
}
}
base.OnActionExecuted(filterContext);
}
}
~/Контроллеры/MyController.sc:
public class MyController : BaseController
{
public ActionResult Index()
{
return View();
}
public ActionResult Details(int id)
{
if (id != 5)
{
ModelState.AddModelError("Error", "Specified row does not exist.");
return RedirectToAction("Error", "Home");
}
else
{
return View("Specified row exists.");
}
}
}
Желаю вам успешных проектов; -)
Ответ 8
Вы можете корректно работать с ошибками, не взламывая global.cs, возиться с HandleErrorAttribute, делать Response.TrySkipIisCustomErrors, подключаться к Application_Error или что-то еще:
В system.web(только обычный, вкл/выкл)
<customErrors mode="On">
<error redirect="/error/401" statusCode="401" />
<error redirect="/error/500" statusCode="500" />
</customErrors>
и в system.webServer
<httpErrors existingResponse="PassThrough" />
Теперь все должно вести себя так, как ожидалось, и вы можете использовать свой ErrorController для отображения того, что вам нужно.
Ответ 9
У меня было все настроено, но я все еще не мог видеть правильные страницы ошибок для кода состояния 500 на нашем промежуточном сервере, несмотря на то, что все хорошо работало на локальных серверах разработки.
Я нашел этот пост в блоге от Рика Стралла, который помог мне.
Мне нужно добавить Response.TrySkipIisCustomErrors = true;
в свой код обработки ошибок.
Ответ 10
Кажется, я опоздал на вечеринку, но вам также лучше проверить это.
Итак, в system.web
для кэширования исключений в приложении, таких как return HttpNotFound()
<system.web>
<customErrors mode="RemoteOnly">
<error statusCode="404" redirect="/page-not-found" />
<error statusCode="500" redirect="/internal-server-error" />
</customErrors>
</system.web>
и system.webServer
для устранения ошибок, которые были обнаружены IIS и не попали в рамки asp.net
<system.webServer>
<httpErrors errorMode="DetailedLocalOnly">
<remove statusCode="404"/>
<error statusCode="404" path="/page-not-found" responseMode="Redirect"/>
<remove statusCode="500"/>
<error statusCode="500" path="/internal-server-error" responseMode="Redirect"/>
</system.webServer>
В последнем случае, если вы беспокоитесь о ответе клиента, измените responseMode="Redirect"
на responseMode="File"
и подайте статический файл html, так как на нем отобразится дружественная страница с кодом ответа 200.
Ответ 11
В web.config добавьте это под тегом system.webserver, как показано ниже,
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404"/>
<remove statusCode="500"/>
<error statusCode="404" responseMode="ExecuteURL" path="/Error/NotFound"/>
<error statusCode="500" responseMode="ExecuteURL"path="/Error/ErrorPage"/>
</httpErrors>
и добавьте контроллер как
public class ErrorController : Controller
{
//
// GET: /Error/
[GET("/Error/NotFound")]
public ActionResult NotFound()
{
Response.StatusCode = 404;
return View();
}
[GET("/Error/ErrorPage")]
public ActionResult ErrorPage()
{
Response.StatusCode = 500;
return View();
}
}
и добавьте их уважаемые взгляды, это будет работать определенно, я думаю, для всех.
Это решение я нашел от: Neptune Century