Ошибка запроса WebClient GET с кодом 401 Unauthorized

Я пытаюсь сделать запрос API GET с приведенным ниже кодом С#, но он не работает с

System.Net.WebException: The remote server returned an error: (401) Unauthorized.
   at System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request)
   at System.Net.WebClient.DownloadString(Uri address)
   at System.Net.WebClient.DownloadString(String address)
   at Rextester.Program.callAPI()
   at Rextester.Program.Main(String[] args)

Обратите внимание, что тот же запрос API работает через почтальона.

private static void callAPI()
{

    WebClient client = new WebClient();
    client.UseDefaultCredentials = false;            

    System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;

    client.Headers.Add("Authorization", "Basic " + Base64Encode("<id:password>"));            
    client.Headers.Add("Content-Type", "application/json");   
    client.Headers.Add("Environment-Id", "325");

    client.QueryString.Add("query", "%7B%7D");

    string reply = client.DownloadString("https://<server-api-url>");

    Console.WriteLine(reply);
}        


private static string Base64Encode(string plainText)
{
    var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
    var encodedS = System.Convert.ToBase64String(plainTextBytes);
    Console.WriteLine(encodedS);
    return encodedS;
}

Еще один странный факт заключается в том, что я вижу исключение, напечатанное перед закодированной строкой. Я должен думать наоборот. Закодированная строка должна быть напечатана раньше, так как вызов метода происходит до вызова API.


person Andy Dufresne    schedule 19.11.2018    source источник
comment
Где запрос API, который работает с Postman? Если вы используете прокси-сервер отладки, такой как Fiddler, чтобы увидеть, что на самом деле происходит, я готов поспорить, что вы обнаружите, что запросы не совпадают.   -  person Panagiotis Kanavos    schedule 19.11.2018
comment
Что представляет собой "<id:password>"? В обычной аутентификации строка должна быть username:password, например $"{username}:{password}" или username + ":" + password или String.Format("{0}:{1}", username,password)   -  person Panagiotis Kanavos    schedule 19.11.2018
comment
Я добавил фиктивные значения, чтобы не раскрывать детали производственной среды. Вы правы насчет формата строки аутентификации. Он имеет формат имя пользователя: пароль.   -  person Andy Dufresne    schedule 19.11.2018
comment
Вы заставляете людей гадать прямо сейчас. Вы используете <> или нет? Если да, то это ошибка. Если нет, то ничто другое не поможет понять, что не так. Как должен выглядеть запрос? Как выглядел запрос почтальона в Fiddler? Как выглядел WebClient? Если заголовки аутентификации отличаются, вы будете знать, что Base64Encode методы неверны. Если есть другое различие, в чем оно?   -  person Panagiotis Kanavos    schedule 19.11.2018
comment
Спасибо, что указали мне правильное направление. Через fiddler я понял, что вызов API фактически перенаправляется (с кодом состояния http 307), и при втором запросе заголовок авторизации не передается. Как включить перенаправления в C#?   -  person Andy Dufresne    schedule 19.11.2018
comment
Просто не используйте WebClient, он устарел с 2010 года и C# 4.0. Он был заменен на HTTP-клиент. В WebClient вам придется перехватить вызов HttpWebRequest, чтобы изменить свойство AllowAutoRedirect. В HttpClient автоперенаправление включено по умолчанию. Вы должны настроить его явно чтобы отключить автоперенаправления.   -  person Panagiotis Kanavos    schedule 19.11.2018
comment
Еще одним огромным преимуществом является то, что HttpClient можно использовать повторно и он потокобезопасен. Вы можете использовать один и тот же HttpClient для нескольких одновременных вызовов, экономя время на разрешении DNS и рукопожатии TLS. Вы можете использовать библиотеки надежности, такие как Polly, с HttpClient как показано в этой статье   -  person Panagiotis Kanavos    schedule 19.11.2018
comment
Мне удалось заставить образец работать с библиотекой RestSharp. Однако проблема с перенаправлением все еще остается. Заголовок авторизации удаляется при перенаправлении. Как предотвратить это с помощью RestSharp?   -  person Andy Dufresne    schedule 19.11.2018
comment
Давайте продолжим обсуждение в чате.   -  person Andy Dufresne    schedule 19.11.2018


Ответы (1)


Получив некоторые указания от @Panagiotis Kanavos, я проследил вызовы API от Fiddler и понял, что вызов API перенаправляется (код состояния http — 307). Клиент C# по умолчанию настроен так, чтобы не передавать заголовок авторизации при перенаправлении. Следовательно, я получил ошибку 401 Unauthorized. Это было исправлено путем добавления проверки кода состояния 401 и повторной отправки запроса API с заголовком авторизации.

Спасибо @Panagiotis Kanavos за помощь

person Andy Dufresne    schedule 22.11.2018