使用 HttpClient 的随机 401 未授权错误

Vin*_*nod 6 c# asp.net-mvc-4 asp.net-web-api dotnet-httpclient iis-8

我正在使用 HttpClient 调用 Web Api REST 端点,偶尔我会看到一个随机的 401 未授权状态。

这是我的 HttpClient 包装器,它是为可重用性目的而创建的。我已经修改了它以保持这篇文章的简单而不修改核心部分。

public class HttpHelper
{
        public const string JsonContentType = "application/json";

        public T PostAsJsonObject<T>(string uri, T t) where T : class
        {
            using (HttpResponseMessage response = PostJsonHttpRequest(uri, JsonContentType, t))
            {
                response.EnsureSuccessStatusCode();
                return response.Content.ReadAsAsync<T>().Result;
            }
        }

        private HttpResponseMessage PostJsonHttpRequest<T>(string uri, string contentType, T content) where T : class
        {
            if (String.IsNullOrEmpty(uri))
            {
                throw new ArgumentNullException("uri");
            }
            HttpResponseMessage response = CreateHttpClient(contentType).PostAsJsonAsync(uri, content).Result;
            return response;
        }

        private HttpClient CreateHttpClient(string contentType)
        {
           var credentials = new NetworkCredential("srvuser", "somepwd", "somedomain");
           return new HttpClient(new HttpClientHandler { Credentials = credentials, PreAuthenticate = true });
        }
}
Run Code Online (Sandbox Code Playgroud)

这是我调用它的方式。

var httpHelper = new HttpHelper();
var response = httpHelper.PostAsJsonObject("https://xyz.pvt/inventory/api/orders", "234");
Run Code Online (Sandbox Code Playgroud)

大多数情况下它工作正常,但偶尔我会收到 401 Unauthorized 错误。我试图找出可能导致它的原因。

System.Net.Http.HttpRequestException:响应状态代码未指示成功:401(未经授权)。在 System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() 在 HttpHelper.PostAsJsonObject[T](String uri, T t)

我查看了asp.net日志,发现了一个奇怪的模式。大多数情况下,我会看到一对条目(401 后跟 200 OK),例如

2017-02-09 12:04:13 104.68.45.152 POST /inventory/api/orders/ - 80 - 132.64.78.120 HTTP/1.1 - 401 2 5 764 
2017-02-09 12:04:16 104.68.45.152 POST /inventory/api/orders/ - 80 somedomain\srvusr 132.64.78.120 HTTP/1.1 - 200 0 0 2917

2017-02-09 12:04:16 104.68.45.152 POST /inventory/api/orders/ - 80 - 132.64.78.120 HTTP/1.1 - 401 2 5 0 
2017-02-09 12:04:19 104.68.45.152 POST /inventory/api/orders/ - 80 somedomain\srvusr 132.64.78.120 HTTP/1.1 - 200 0 0 2230
Run Code Online (Sandbox Code Playgroud)

但偶尔我只看到 401

2017-02-09 12:14:04 104.68.45.152 POST /inventory/api/orders/ - 80 - 132.64.78.120 HTTP/1.1 - 401 2 5 0
Run Code Online (Sandbox Code Playgroud)

REST api 是使用在 .NET 4.6.1 和 IIS 7.5 上运行的 ASP.NET Web Api 2 实现的,它仅使用 Windows 身份验证进行设置。

不确定是什么导致身份验证偶尔失败。

更新:

这是我使用异步等待模式编写的新代码。

public class HttpHelper
{
        public const string JsonContentType = "application/json";

        private HttpClient CreateHttpClient(string url, string contentType)
        {
            var handler = new HttpClientHandler
            {
                Credentials = new CredentialCache { { new Uri(url), "NTLM", new NetworkCredential("srvuser", "somepwd", "somedomain") } },
                PreAuthenticate = true
            };
            var httpClient = new HttpClient(handler);
            httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(contentType));
            return httpClient;
        }

        private async Task<HttpResponseMessage> PostJsonHttpRequestAsync<T>(string url, string contentType, T content) where T : class
        {
            if (string.IsNullOrEmpty(url))
            {
                var exception = new ArgumentNullException("url");
                throw exception;
            }

            var response = await CreateHttpClient(url, contentType).PostAsJsonAsync(url, content);

            return response;
        }

        public async Task<T> PostAsJsonObjectAsync<T>(string uri, T t) where T : class
        {
            using (var response = await PostJsonHttpRequestAsync(uri, JsonContentType, t))
            {
                return await response.Content.ReadAsAsync<T>(new[]
                    {
                      new JsonMediaTypeFormatter
                      {
                         SerializerSettings = { NullValueHandling = NullValueHandling.Ignore, TypeNameHandling = TypeNameHandling.Auto }
                      }
                    }.AsEnumerable());
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

这是我最终调用它的方式。

static class TestMe
{
    static void Main()
    {
        var httpHelper = new HttpHelper();
        var responseTask = httpHelper.PostAsJsonObjectAsync("https://xyz.pvt/inventory/api/orders", "234");

        responseTask.Wait(120000);
        var response = responseTask.Result;
        Console.WriteLine(response);
    }
}
Run Code Online (Sandbox Code Playgroud)

即使现在我也看到同样的问题。仍然随机出现 401 Unauthorized 错误。

更新 2

我做了更多调查,发现这些神秘的 401 错误来自 IIS。当这些神秘的 401 错误发生时,IIS 日志中有一个相应的 401 错误条目,但在这种情况下,请求永远不会到达 asp.net 管道。好像出于某种原因,请求被 IIS 以 401 错误退回。

无需深入研究,我知道 IIS 中的 Windows 身份验证使用 2 个请求来管理基于 HttpClient 的配置方式的身份验证。因此,成功的身份验证请求将具有 401.2 后跟 200 OK。这是大多数时候发生的事情。有时你我会注意到有一个 401.2,然后是 401。重试时相同的请求会通过(401.2 后跟 200 OK)。

如果有人对这个问题有一些洞察力和潜在的解决方案,真的很感激。

Joe*_*rby -2

您确定您的代码是问题所在吗?我不知道您的具体情况,但网络服务器有时可能会无缘无故地向您发送 401 响应。

您可以执行以下操作来测试该理论:同时运行程序的两个(或更多)实例,最好是在不同的机器上。等到出现 401 错误为止。如果代码的所有实例同时开始收到 401 错误,那么几乎可以肯定是 Web 服务器上出现了问题。