Asp.net mvc 在Asp.NETMVC2中,有没有更好的方法来返回401状态代码而不获取身份验证重定向

Asp.net mvc 在Asp.NETMVC2中,有没有更好的方法来返回401状态代码而不获取身份验证重定向,asp.net-mvc,rest,asp.net-mvc-2,Asp.net Mvc,Rest,Asp.net Mvc 2,我的站点有一部分具有轻量级xml/json REST API。我的大多数站点都支持forms auth,但只有一些API操作需要身份验证 我的API有一个自定义的authorized属性,用于检查某些权限,当它失败时,将导致401。一切都很好,除了因为我使用forms auth,Asp.net方便地将其转换为302重定向到我的登录页面 我已经看到了前面的一些问题,这些问题要么返回403,要么在global.asaxprotected void Application_EndRequest()中加

我的站点有一部分具有轻量级xml/json REST API。我的大多数站点都支持forms auth,但只有一些API操作需要身份验证

我的API有一个自定义的authorized属性,用于检查某些权限,当它失败时,将导致401。一切都很好,除了因为我使用forms auth,Asp.net方便地将其转换为302重定向到我的登录页面

我已经看到了前面的一些问题,这些问题要么返回403,要么在global.asaxprotected void Application_EndRequest()中加入一些逻辑,这似乎有点不妥 这将基本上把302转换成401,只要它符合任何标准

我现在做的有点像一个问题,但不是检查302的应用程序_EndRequest(),而是让authorize属性返回666,这表明我需要将其设置为401

这是我的密码:

protected void Application_EndRequest()
{
  if (Context.Response.StatusCode == MyAuthAttribute.AUTHORIZATION_FAILED_STATUS)
   {   
       //check for 666 - status code of hidden 401
        Context.Response.StatusCode = 401;
    }
 }

即使这样做有效,我的问题是,Asp.net MVC 2中是否有什么东西可以阻止我这样做?或者,一般来说,有更好的方法吗?我认为这对于任何使用REST api的人或只是在控制器中执行ajax请求的人来说都会有很大的帮助。您最不希望做的事情是执行请求并获取登录页面的内容,而不是json。

如何使用自定义筛选器装饰控制器/操作:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class RequiresAuthenticationAttribute : FilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var user = filterContext.HttpContext.User;
        if (!user.Identity.IsAuthenticated)
        {
            filterContext.HttpContext.Response.StatusCode = 401;
            filterContext.HttpContext.Response.End();
        }
    }
}
在控制器中:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [RequiresAuthentication]
    public ActionResult AuthenticatedIndex()
    {
        return View();
    }
}

我找到的最简单、最干净的解决方案是使用jQuery.ajaxSuccess()事件注册回调,并检查“X-AspNetMvc-Version”响应头

我的应用程序中的每个jQuery Ajax请求都由Mvc处理,因此如果缺少标题,我知道我的请求已重定向到登录页面,我只需重新加载页面以进行顶级重定向:

 $(document).ajaxSuccess(function(event, XMLHttpRequest, ajaxOptions) {
    // if request returns non MVC page reload because this means the user 
    // session has expired
    var mvcHeaderName = "X-AspNetMvc-Version";
    var mvcHeaderValue = XMLHttpRequest.getResponseHeader(mvcHeaderName);

    if (!mvcHeaderValue) {
        location.reload();
    }
});
页面重新加载可能会导致一些Javascript错误(取决于您对Ajax响应所做的操作),但在大多数情况下,如果调试处于关闭状态,用户将永远不会看到这些错误

如果您不想使用内置标题,我相信您可以轻松添加一个自定义标题,并遵循相同的模式。

关闭方向提示 从MSDN中,解释如何避免401响应的重定向:)

引用:

使用IIS管理器,右键单击 WinLogin.aspx文件,单击属性, 然后转到“自定义错误”选项卡 编辑各种401错误和 指定自定义重定向。 不幸的是,这种重定向必须 是一个静态文件,它不会处理 ASP.NET页面。我的解决办法是 重定向到静态重定向401.htm 文件,具有完整的物理路径, 其中包含javascript或 元标记,重定向到真实的 ASP.NET登录表单,名为 WebLogin.aspx。请注意,您丢失了 这些文件中的原始返回URL 重定向,因为IIS错误 重定向需要一个静态html 没有动态文件,因此您将 以后再处理


希望对您有所帮助。

我仍在使用结束请求技术,所以我想我会给出答案,但确实如此 一般来说,我认为这是迄今为止最好的答案

protected void Application_EndRequest()
{
  if (Context.Response.StatusCode == MyAuthAttribute.AUTHORIZATION_FAILED_STATUS)
   {   
       //check for 666 - status code of hidden 401
        Context.Response.StatusCode = 401;
    }
 }

另一种方法是实现自定义的
ActionResult
。在我的例子中,我无论如何都想要一个,因为我想要一种简单的方法,用自定义的头和响应代码发送数据(对于RESTAPI)。我找到了做一个调用的想法,并简单地添加了对
response.End()
的调用。结果如下:

public class DelegatingActionResult : ActionResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        Command(context);
        // prevent ASP.Net from hijacking our headers
        context.HttpContext.Response.End();
    }

    private readonly Action<ControllerContext> Command;

    public DelegatingActionResult(Action<ControllerContext> command)
    {
        if (command == null)
            throw new ArgumentNullException("command");

        Command = command;
    }
}
public类DelegatingActionResult:ActionResult
{
公共覆盖无效ExecuteSult(ControllerContext上下文)
{
if(上下文==null)
抛出新的ArgumentNullException(“上下文”);
命令(上下文);
//防止ASP.Net劫持我们的头文件
context.HttpContext.Response.End();
}
私有只读操作命令;
公共DelegatingActionResult(操作命令)
{
如果(命令==null)
抛出新的ArgumentNullException(“命令”);
命令=命令;
}
}

没错,我就是这么做的,但是asp.net会接受401并将其转换为302重定向到登录页面。我需要能够绕过重定向。您注意到
filterContext.HttpContext.Response.End()了吗呼叫?这可能有效,但似乎会导致其他问题。比如抛出未捕获的异常:“发送HTTP头后无法重定向。”谢谢,但我正在寻找一个没有这些副作用的更干净的解决方案。谢谢你的评论。我不确定这是否真的比我已经做的好很多。我不想同时避免重定向,我只想更好地控制api。我现在所做的很有效,似乎有更好的方法。