Как отобразить пользовательские страницы ошибок в Asp.Net Mvc 3? - PullRequest
23 голосов
/ 18 июля 2011

Я хочу, чтобы все ошибки 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 для проверки ответа, я вижу, что пока страница пуста, в заголовках возвращается статус 401

До сих пор я пытался использовать предложения этого SO ответа , поскольку я использую IIS Express, но безрезультатно. Я попытался использовать комбинацию <custom errors> и <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");
}

Ответы [ 4 ]

34 голосов
/ 08 сентября 2011

Я использую эти шаги:

// 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);
        }
    }

И представление индекса в папке ошибок:

@* 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>

И - последний шаг:

<!-- in web.config: -->

<customErrors mode="Off"/>
10 голосов
/ 18 июля 2011

Мне так и не удалось заставить 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 сейчас не обрабатывается, но вы можете добавить его довольно легко.

9 голосов
/ 10 сентября 2011

Может быть, я что-то упускаю, но у 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.

0 голосов
/ 14 сентября 2011

Глядя на первую часть вашего web.config, вы указываете прямо на страницу .aspx. Когда я настраивал свои страницы ошибок, я прямо указывал на контроллер и действие. Например:

<customErrors mode="On" defaultRedirect="~/Error/UhOh">
  <error statusCode="404" redirect="~/Error/NotFound" />
  <error statusCode="403" redirect="~/Error/AccessDenied" />
</customErrors>

И у меня был контроллер ошибок со всеми необходимыми действиями. Я не думаю, что MVC хорошо работает с прямыми вызовами на страницы .aspx.

...