使用 HttpResponseMessage 401 在 ajax 帖子上总是成功

Posted

技术标签:

【中文标题】使用 HttpResponseMessage 401 在 ajax 帖子上总是成功【英文标题】:Always success on ajax post with HttpResponseMessage 401 【发布时间】:2014-01-06 21:28:29 【问题描述】:

我总是在客户端的 ajax 帖子上收到 statusCode=200,而服务器则以 HttpStatusCode.Unauthorized 回答。

我的控制器代码:

public class AccountApiController : ApiController

    public HttpResponseMessage Login(HttpRequestMessage request, [FromBody]LoginViewModel loginModel)
    
        return request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Unauthorized login.");
    

我的 ajax 代码:

$.ajax(
    url: '/api/accountapi/login',
    type: 'POST',
    data: data
    )
    .done(function (object, status, xhr) 
        alert("Success: " + xhr.status + " : " + xhr.statusText);
    )
    .always(function (object) 
        $("#output").text(JSON.stringify(object, null, 4));
    );

结果:带有文本Success: 200 : OK 的警报 和输出窗口:


    "Message": "Unauthorized login."

所以,我可以得到文本错误消息,但我需要得到 HttpStatusCode 来处理错误语句。请帮帮我。

有关此问题的更多信息以及 Brock Allen 提供的优雅解决方案: http://brockallen.com/2013/10/27/using-cookie-authentication-middleware-with-web-api-and-401-response-codes/

【问题讨论】:

使用您的开发者工具查看请求/响应属性。在 chrome 中转到网络选项卡,发出请求,检查请求 URL 和状态代码。 我得到了:Request URL:http://localhost:31757/api/accountapi/login Request Method:POST Status Code:200 OKX-Responded-JSON:"status":401,"headers":"location":"http:\/\/localhost:31757\/Account\/Login?ReturnUrl=%2Fapi%2Faccountapi%2Flogin"。 JSON 答案看起来是正确的挖掘方法:) 【参考方案1】:

因为返回的状态为 200,但用户未被授权,另一种选择是在您的 javascript 中检查状态为 401 的 X-Responded-JSON。

.done(function (object, status, xhr) 
      if (xhr.getResponseHeader("X-Responded-JSON") != null 
          && JSON.parse(xhr.getResponseHeader("X-Responded-JSON")).status == "401") 
          //some message here
          return;
     

【讨论】:

【参考方案2】:

您的页面可能会在您返回HttpStatusCode.Unauthorized 后立即被表单身份验证模块重定向到登录页面。

来自 MSDN:

所有未经身份验证的用户都被拒绝访问您的任何页面 应用。如果未经身份验证的用户尝试访问页面,则 表单身份验证模块将用户重定向到登录页面 由表单元素的 loginUrl 属性指定。

登录页面,或者它被重定向到的任何页面,然后提供状态代码 200。

【讨论】:

一切都设置为允许匿名。但是页面无论如何都不会重定向:( 作为测试,尝试将 StatusCode 更改为 Unauthorized 以外的任何值,看看是否有效。我担心这是表单身份验证模块中的设计。 我怀疑你覆盖了allowanonymous,因为你无论如何都返回Unauthorize,所以它们毕竟是不允许的。 Magnus,谢谢,404 和 403 按预期工作。所以问题只存在于 401。 乐于助人!遗憾的是它没有解决您的问题,但希望它提供了您需要继续进行的内容:)【参考方案3】:

详细说明 Valin 的评论,并内联 Brock Allen 的解决方案。

线索在于返回的 OK 响应,它在内部将重定向捕获到表单登录:

<b>X-Responded-JSON</b>: "status": 401, "headers": "location":"http:\/\/localhost:50004\/Login?ReturnUrl=%2FClient"

如果您想在服务器上进行修复,而不是抓取此内部错误状态的响应,您可以使用 Brock Allen 在Using cookie authentication middleware with Web API and 401 response codes 上的文章中的解决方案:

通常在使用 cookie 身份验证中间件时,当服务器(MVC 或 WebForms)发出 401 时,响应将转换为 302 重定向到登录页面(由 CookieAuthenticationOptions 上的 LoginPath 配置)。但是当进行 Ajax 调用并且响应是 401 时,将 302 重定向返回到登录页面是没有意义的。相反,您只希望返回 401 响应。不幸的是,这不是我们通过 cookie 中间件获得的行为——响应更改为 200 状态代码,带有 JSON 响应正文和消息:

"Message":"Authorization has been denied for this request."

我不确定此功能的要求是什么。要更改它,您必须通过在 cookie 身份验证中间件上配置 CookieAuthenticationProvider 来控制出现 401 未授权响应时的行为:

app.UseCookieAuthentication(new CookieAuthenticationOptions

   AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
   LoginPath = new PathString("/Account/Login"),
   Provider = new CookieAuthenticationProvider
   
      OnApplyRedirect = ctx =>
      
         if (!IsAjaxRequest(ctx.Request))
         
            ctx.Response.Redirect(ctx.RedirectUri);
         
     
   
);

注意它处理 OnApplyRedirect 事件。当调用不是 Ajax 调用时,我们重定向。否则,我们什么也不做,让 401 返回给调用者。

IsAjaxRequest 的检查只是从 katana 项目中的 helper 复制而来:

private static bool IsAjaxRequest(IOwinRequest request)

   IReadableStringCollection query = request.Query;
   if ((query != null) && (query["X-Requested-With"] == "XMLHttpRequest"))
   
      return true;
   
   IHeaderDictionary headers = request.Headers;
   return ((headers != null) && (headers["X-Requested-With"] == "XMLHttpRequest"));

【讨论】:

这是我一直在寻找的答案。出色的解决方案 - 感谢您的分享!

以上是关于使用 HttpResponseMessage 401 在 ajax 帖子上总是成功的主要内容,如果未能解决你的问题,请参考以下文章

HttpResponseMessage 你处理了吗?

无法使用 UTF8 编码转换 HttpResponseMessage

使用 HttpResponseMessage 401 在 ajax 帖子上总是成功

将内容放在 HttpResponseMessage 对象中?

构建 ASP.NET Web Api 时是不是必须使用 HTTP 协议和 HttpResponseMessage 类?

如何从 HttpResponseMessage 中获取特定的标头值