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

时间:2017-03-03 18:36:37

标签: c# asp.net-mvc-4 asp.net-web-api iis-8 dotnet-httpclient

我正在使用HttpClient来调用Web Api REST端点,偶尔我会看到一个随机的401 Unauthorized Status。

这是我的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 });
        }
}

以下是我如何调用它。

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

大部分时间它工作正常,但有一段时间我得到401 Unauthorized错误。我试图找出可能导致它的原因。

  

System.Net.Http.HttpRequestException:响应状态代码没有   表示成功:401(未经授权)。在   System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()at   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

但偶尔我会看到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

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

不确定导致身份验证偶尔失败的原因。

更新

这是我使用async await pattern编写的新代码。

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

以下是我最终如何调用它。

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

即使是现在我也看到同样的问题。仍然随机获得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)。

真的很感激,如果有人对这个问题有一些了解,并且可以解决这个问题。

1 个答案:

答案 0 :(得分:-2)

您确定您的代码甚至是问题吗?我不知道您的情况的详细信息,但Web服务器有时可能会无缘无故地向您发送401回复。

您可以采取以下措施来测试此理论:同时运行程序的两个(或更多)实例,最好是在不同的计算机上运行。等到你得到401错误。如果您的代码的所有实例同时开始收到401错误,那么它几乎肯定是Web服务器上的问题。