简体   繁体   中英

Random 401 Unauthorized errors using HttpClient

I am using HttpClient to invoke a Web Api REST endpoint and once in a while I see a random 401 Unauthorized Status.

Here is my HttpClient wrapper that was created for re usability purposes. I have modified it to keep it simple for this post without modifying the core parts.

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

Here is how I am invoking it.

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

Most of the time it works fine, but once in a while I get 401 Unauthorized error. I am trying to figure out what might be causing it.

System.Net.Http.HttpRequestException: Response status code does not indicate success: 401 (Unauthorized). at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() at HttpHelper.PostAsJsonObject[T](String uri, T t)

I looked at asp.net logs, and found a strange pattern. Most of the time, I see pair of entries (401 followed by 200 OK) such as

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

But occasionally I just see 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 is implemented using ASP.NET Web Api 2 running on .NET 4.6.1 and in IIS 7.5, it is setup with just Windows Authentication.

Not sure what is causing the authentication to fail once in a while.

UPDATE:

Here is the new code that I wrote using 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());
            }
        }
    }

Here is how I am finally invoking it.

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

Even now I see same problem. Still getting random 401 Unauthorized errors.

UPDATE 2

I did some more investigation and found that these mysterious 401 errors are coming from IIS. When ever these mysterious 401 error occurs, IIS log has a corresponding 401 error entry, but in this case the request never reaches asp.net pipeline. As if for some reason the request is bounced back by IIS with 401 error.

Without going into too much in depth, I know that windows authentication in IIS uses 2 requests to manage authentication based on how the HttpClient is configured. So a successful authentication request will have a 401.2 followed by 200 OK. It is what happening most of the time. Occasionally thou I notice there is a 401.2, followed by just 401. The same request upon retry attempt goes thru fine (401.2 followed by 200 OK).

Really appreciate if anyone have some insight into this issue and a potential resolution to it.

Are you certain that your code is even the problem? I don't know the details of your situation, but it's possible the web server is sometimes sending you back 401 responses for no good reason.

Here's something you could do to test this theory: run two (or more) instances of your program simultaneously, ideally on different machines. Wait until you get the 401 errors. If all instances of your code start receiving the 401 errors at the same time, then it's almost certainly a problem on the web server.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM