【问题标题】:ASP.NET MVC user friendly 401 errorASP.NET MVC 用户友好的 401 错误
【发布时间】:2010-12-13 09:16:41
【问题描述】:

我已经在 ASP.NET MVC 站点 in a way like suggests this post 中实现了错误处理。

出现 404 错误时一切正常。但是对于 401 错误,如何正确显示用户友好的屏幕?它们通常不会抛出可以在Application_Error() 中处理的异常,而是操作返回 HttpUnauthorizedResult。一种可能的方法是将以下代码添加到 Application_EndRequest() 方法的末尾

if (Context.Response.StatusCode == 401)
{
    throw new HttpException(401, "You are not authorised");
    // or UserFriendlyErrorRedirect(new HttpException(401, "You are not authorised")), witout exception
}

但在Application_EndRequest() Context.Session == null 内部,errorController.Execute() 失败,因为它不能使用默认的 TempDataProvider。

  // Call target Controller and pass the routeData.
  IController errorController = new ErrorController();
  errorController.Execute(new RequestContext(    
       new HttpContextWrapper(Context), routeData)); // Additional information: The SessionStateTempDataProvider requires SessionState to be enabled.

那么,您能否建议一些如何在 ASP.NET MVC 应用程序中“用户友好地处理”401 的最佳实践?

谢谢。

【问题讨论】:

标签: asp.net-mvc handleerror


【解决方案1】:

在我的一个项目中,我使用来自uvita 的代码。

我有 ASP.NET MVC2 并且我使用 Active Directory 身份验证而没有登录页面。 我有一个使用站点母版页的 NoAuth.aspx 页面,集成了 Web 应用程序布局。

这是 web.config。

<system.web>
    <authentication mode="Windows" />
    <roleManager enabled="true" defaultProvider="AspNetWindowsTokenRoleProvider">
       <providers>
          <clear />
          <add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider"
          applicationName="/" />
      </providers>
    </roleManager>
</system.web>

新类 CustomAutorizeAttribute

using System.Web.Mvc;
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
        else
        {
           filterContext.Result = new ViewResult { ViewName = "NoAuth"};
        }
    }
}

和控制器

[CustomAuthorize(Roles = "ADRole")]
public class HomeController : Controller
{
    public HomeController()
    {
    }
}

【讨论】:

    【解决方案2】:

    我设法以一种非常简单的方式解决了这个问题。我想为登录用户显示一个自定义页面(“你没有权限 bla bla ...”)并将未经身份验证的用户重定向到登录页面(默认行为)。 因此,我实现了一个自定义 AuthorizeAttribute(比如 CustomAuthorizeAttribute),并覆盖了 HandleUnauthorizedRequest 方法,如果用户通过身份验证,我会使用名为 AccessDenied.aspx 的 ViewResult(在共享文件夹中)设置 filterContext 参数的 Result 属性

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
        else
        {
            filterContext.Result = new ViewResult { ViewName = "AccessDenied" };
        }
    }
    

    那么你必须改用这个新属性。 问候。

    【讨论】:

    • 对于任何想知道的人,此属性仅在 MSDNs Framework 4.0 中提及。它在 3.5 中不可用
    • 这可能不适用于“windows”身份验证。试过了,不幸的是失败了..
    【解决方案3】:

    如果您使用的是 ASP.NET MVC,那么您很可能会使用 IIS,那么为什么不直接设置 IIS 以将您的自定义 401 错误页面用于该 Web 应用程序/虚拟目录?

    【讨论】:

    • 我需要完全回发和 AJAX 请求的不同行为以及对错误处理的更多控制
    • 因为您可能希望更精细地控制 404 的呈现方式(HTML 和 HTTP)。您可能还需要 404 页面上的其他信息,例如只能在您的应用程序中使用的动态内容。您可能希望使用您的应用程序日志引擎记录特定消息并跟踪 404 路径 - 所以这一切都必须在应用程序内部完成。
    【解决方案4】:

    查看 HandleErrorAttribute。继承它或添加您自己的实现来处理您感兴趣的所有状态代码。您可以让它为每种错误类型返回一个单独的错误视图。

    这里是如何创建您的句柄错误异常过滤器的想法。我已经扔掉了大部分东西,只专注于我们的必需品。绝对看看原始实现以添加参数检查和其他重要的东西。

    public class HandleManyErrorsAttribute : FilterAttribute, IExceptionFilter
    {
        public virtual void OnException(ExceptionContext filterContext)
        {
            if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
                return;
    
            Exception exception = filterContext.Exception;
    
            string viewName = string.Empty;
            object viewModel = null;
            int httpCode = new HttpException(null, exception).GetHttpCode();
            if (httpCode == 500)
            {
                viewName = "Error500View";
                viewModel = new Error500Model();
            }
            else if (httpCode == 404)
            {
                viewName = "Error404View";
                viewModel = new Error404Model();
            }
            else if (httpCode == 401)
            {
                viewName = "Error401View";
                viewModel = new Error401Model();
            }
    
            string controllerName = (string)filterContext.RouteData.Values["controller"];
            string actionName = (string)filterContext.RouteData.Values["action"];
            filterContext.Result = new ViewResult
            {
                ViewName = viewName,
                MasterName = Master,
                ViewData = viewModel,
                TempData = filterContext.Controller.TempData
            };
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.Clear();
            filterContext.HttpContext.Response.StatusCode = httpCode;
    
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
        }
    }
    

    然后你用这个属性“装饰”你的控制器动作:

    [HandleManyErrors]
    public ActionResult DoSomethingBuggy ()
    {
        // ...
    }
    

    【讨论】:

    • 据我了解,此解决方案需要使用 HandleManyErrors 属性装饰每个控制器或潜在的安全操作(可能导致 401 的操作)。但是最好有全局 401 处理(例如在 Global.asax 中),或者我不对,这是个坏主意?
    • 使用 MVC 模型,您并不总是事先知道路线是否可以。路由可以解析以达到某些控制器操作,但在使用数据库检查提供的参数后,您可能会决定使用此参数的路由无法检索所需的资源,因此您返回 4xx 错误。通过这种动态行为,可以更轻松地检查事物并从控制器操作中抛出 HttpException,因为它们知道它们将由属性处理以返回错误页面。
    • 对于静态行为,您可以添加最后一个“catchall”路由以匹配之前定义的规则未捕获的所有路由。包罗万象将调用一些将简单地抛出 HttpException 的控制器操作。这样,您的 Global.asax 中就有静态错误处理逻辑,并直接在相应的控制器操作中进行动态错误处理。
    • @Roman 你总是可以创建一个 BaseController 并将属性附加到它。
    • @Roman 只需在您的项目中编辑App_Start\FilterConfig.cs 并在RegisterGlobalFilters 覆盖中添加一行,如filters.Add(new HandleManyErrorsAttribute());。这将把它应用到全面的每一个行动中。
    猜你喜欢
    • 1970-01-01
    • 2013-03-02
    • 2013-02-14
    • 1970-01-01
    • 2012-09-16
    • 1970-01-01
    • 1970-01-01
    • 2011-08-23
    • 1970-01-01
    相关资源
    最近更新 更多