Как отображать пользовательские страницы ошибок в Asp.Net Mvc 3?
Я хочу, чтобы все ошибки 401 были перенаправлены на пользовательскую страницу ошибок. Первоначально я настроил следующую запись в моем файле web.config.
<customErrors defaultRedirect="ErrorPage.aspx" mode="On">
<error statusCode="401" redirect="~/Views/Shared/AccessDenied.aspx" />
</customErrors>
При использовании IIS Express я получаю стандартную страницу ошибки IIS Express 401.
Если IIS Express не используется, возвращается пустая страница. Используя вкладку Google Chrome Network для проверки ответа, я вижу, что пока страница пуста, в заголовках возвращается статус 401
До сих пор я пробовал использовать предложения из этого SO ответа, поскольку я использую IIS Express, но безрезультатно. Я попытался использовать комбинацию <custom errors>
и <httpErrors>
- стандартная ошибка или пустая страница по-прежнему отображается.
На httpErrors
раздел httpErrors
выглядит так, основываясь на ссылке из приведенного выше SO вопроса (я также нашел другой очень многообещающий ответ, однако не повезло - пустой ответ)
<system.webServer>
<httpErrors errorMode="DetailedLocalOnly" existingResponse="PassThrough" >
<remove statusCode="401" />
<error statusCode="401" path="/Views/Shared/AccessDenied.htm" />
</httpErrors>
<!--
<httpErrors errorMode="Custom"
existingResponse="PassThrough"
defaultResponseMode="ExecuteURL">
<remove statusCode="401" />
<error statusCode="401" path="~/Views/Shared/AccessDenied.htm"
responseMode="File" />
</httpErrors>
-->
</system.webServer>
Я даже изменил файл applicationhost.config
и изменил <httpErrors lockAttributes="allowAbsolutePathsWhenDelegated,defaultPath">
на <httpErrors lockAttributes="allowAbsolutePathsWhenDelegated">
на основе информации из iis.net. В ходе моих усилий мне также удалось наткнуться на эту ошибку, как описано в другом вопросе SO.
Как отобразить пользовательские страницы ошибок в Asp.Net Mvc 3?
Дополнительная информация
Следующие действия контроллера были украшены атрибутом Authorize
для конкретного пользователя.
[HttpGet]
[Authorize(Users = "domain\\userXYZ")]
public ActionResult Edit()
{
return GetSettings();
}
[HttpPost]
[Authorize(Users = "domain\\userXYZ")]
public ActionResult Edit(ConfigurationModel model, IList<Shift> shifts)
{
var temp = model;
model.ConfiguredShifts = shifts;
EsgConsole config = new EsgConsole();
config.UpdateConfiguration(model.ToDictionary());
return RedirectToAction("Index");
}
Ответы
Ответ 1
Я использую следующие шаги:
// in Global.asax.cs:
protected void Application_Error(object sender, EventArgs e) {
var ex = Server.GetLastError().GetBaseException();
Server.ClearError();
var routeData = new RouteData();
routeData.Values.Add("controller", "Error");
routeData.Values.Add("action", "Index");
if (ex.GetType() == typeof(HttpException)) {
var httpException = (HttpException)ex;
var code = httpException.GetHttpCode();
routeData.Values.Add("status", code);
} else {
routeData.Values.Add("status", 500);
}
routeData.Values.Add("error", ex);
IController errorController = new Kavand.Web.Controllers.ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
protected void Application_EndRequest(object sender, EventArgs e) {
if (Context.Response.StatusCode == 401) { // this is important, because the 401 is not an error by default!!!
throw new HttpException(401, "You are not authorised");
}
}
и
// in Error Controller:
public class ErrorController : Controller {
public ActionResult Index(int status, Exception error) {
Response.StatusCode = status;
return View(status);
}
protected override void Dispose(bool disposing) {
base.Dispose(disposing);
}
}
И индексный указатель в папке Error:
@* in ~/Views/Error/Index.cshtml: *@
@model Int32
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Kavand | Error</title>
</head>
<body>
<div>
There was an error with your request. The error is:<br />
<p style=" color: Red;">
@switch (Model) {
case 401: {
<span>Your message goes here...</span>
}
break;
case 403: {
<span>Your message goes here...</span>
}
break;
case 404: {
<span>Your message goes here...</span>
}
break;
case 500: {
<span>Your message goes here...</span>
}
break;
//and more cases for more error-codes...
default: {
<span>Unknown error!!!</span>
}
break;
}
</p>
</div>
</body>
</html>
AND - последний шаг:
<!-- in web.config: -->
<customErrors mode="Off"/>
Ответ 2
Я никогда не мог получить CustomErrors в web.config и MVC, чтобы хорошо играть вместе, поэтому я сдался. Я делаю это вместо этого.
В global.asax:
protected void Application_Error()
{
var exception = Server.GetLastError();
var httpException = exception as HttpException;
Response.Clear();
Server.ClearError();
var routeData = new RouteData();
routeData.Values["controller"] = "Errors";
routeData.Values["action"] = "General";
routeData.Values["exception"] = exception;
Response.StatusCode = 500;
if (httpException != null)
{
Response.StatusCode = httpException.GetHttpCode();
switch (Response.StatusCode)
{
case 403:
routeData.Values["action"] = "Http403";
break;
case 404:
routeData.Values["action"] = "Http404";
break;
}
}
// Avoid IIS7 getting in the middle
Response.TrySkipIisCustomErrors = true;
IController errorsController = new GNB.LG.StrategicPlanning.Website.Controllers.ErrorsController();
HttpContextWrapper wrapper = new HttpContextWrapper(Context);
var rc = new RequestContext(wrapper, routeData);
errorsController.Execute(rc);
}
В ErrorsController:
public class ErrorsController
{
public ActionResult General(Exception exception)
{
// log the error here
return View(exception);
}
public ActionResult Http404()
{
return View("404");
}
public ActionResult Http403()
{
return View("403");
}
}
В web.config:
<customErrors mode="Off" />
Это работало для меня независимо от того, где и как создается ошибка. 401 здесь не обрабатывается, но вы можете добавить его довольно легко.
Ответ 3
Возможно, я что-то пропустил, но MVC имеет глобальный ErrorHandlerAttribute
по умолчанию, который использует пользовательские ошибки. Это объясняется довольно хорошо здесь.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
Все, что вам нужно сделать, это включить custom errors
в конфиге, а затем настроить настраиваемые переадресации ошибок, предпочтительно в статический файл HTML
(в случае возникновения ошибок в приложении).
<customErrors mode="On" defaultRedirect="errors.htm">
<error statusCode="404" redirect="errors404.htm"/>
</customErrors>
Если вы предпочитаете, также можете указать пользовательский Controller
, чтобы отобразить ошибки. В следующем примере я только использовал маршрутизацию по умолчанию для Controller
с именем Error
с действием под названием Index
и строковым параметром с именем id
(для получения кода ошибки). Конечно, вы можете использовать любую маршрутизацию, которую вы желаете. Ваш пример не работает, потому что вы пытаетесь напрямую связать себя с каталогом Views
, не переходя через Controller
. MVC.NET не отправляет запросы непосредственно в папку Views
.
<customErrors mode="On" defaultRedirect="/error/index/500">
<error statusCode="404" redirect="/error/index/404"/>
</customErrors>
ErrorHandlerAttribute также можно широко использовать с Controllers/Actions
для перенаправления ошибок с именем Views
, связанным с Controller
. Например, чтобы показать View
с именем MyArgumentError
, когда возникает исключение типа ArgumentException
, вы можете использовать:
[ControllerAction,ExceptionHandler("MyArgumentError",typeof(ArgumentException))]
public void Index()
{
// some code that could throw ArgumentExcepton
}
Конечно, другой вариант - обновить страницу запаса Error
в Shared
.
Ответ 4
Посмотрев на первую часть вашего web.config, вы указываете на .aspx-страницу напрямую. Когда я настраиваю страницы ошибок, я указывал непосредственно на контроллер и действие. Например:
<customErrors mode="On" defaultRedirect="~/Error/UhOh">
<error statusCode="404" redirect="~/Error/NotFound" />
<error statusCode="403" redirect="~/Error/AccessDenied" />
</customErrors>
И у меня был контроллер Error со всеми необходимыми действиями. Я не думаю, что MVC хорошо играет с прямыми вызовами на страницы .aspx.