Пустые данные ответа с использованием HttpWebResponse с Mono в Ubuntu

Я пытаюсь запустить программу .NET в Ubuntu, используя моно.

Программа подключается к API удаленного сервера и получает в ответ строку XML. Вот упрощенная версия функции, отвечающей за эту задачу.

 using System.Net;

 static string GetProducts(string clientID, string apiKey, string url)
    {
        HttpWebRequest req = WebRequest.Create(url) as HttpWebRequest;
        req.Credentials = new NetworkCredential(clientID, apiKey);
        req.ContentType = "application/x-www-form-urlencoded";

        string result = string.Empty;

        using (HttpWebResponse resp = req.GetResponse() as HttpWebResponse)
        {
            StreamReader reader = new StreamReader(resp.GetResponseStream());
            result = reader.ReadToEnd();
        }

        return result;
    }

Он отлично работает на моей машине с Windows 8.1. Но моя цель — запустить его на Ubuntu VPS. Я использую моно для достижения этого. Программа запускается, но останавливается с исключением, когда пытается разобрать загруженную строку XML.

[ERROR] FATAL UNHANDLED EXCEPTION: System.Xml.XmlException: Document element did not appear.  Line 1, position 1.

Немного покопавшись, я обнаружил, что программа на самом деле не получает ответ в формате XML, а вместо этого выдает пустую строку. Что странно, так как ошибок подключения не возникает.

У меня был некоторый предыдущий опыт работы с моно и Ubuntu, но я никогда раньше не сталкивался с такой проблемой.

Может ли это быть как-то связано с сервером Ubuntu или моно? Или в самом коде?

Есть мысли по этому поводу?


person Aceonace    schedule 07.04.2014    source источник
comment
Вы можете попробовать что-то вроде NetTool для анализа трафика Ubuntu, а затем сравнить его с трафиком, который вы видите на Fiddler в Windows. Возможно, есть некоторые заголовки/настройки по умолчанию, на которые вы полагаетесь в Windows, которые, возможно, необходимо явно установить в Linux.   -  person cgotberg    schedule 07.04.2014
comment
@cgotberg После некоторого тестирования я обнаружил, что отправленные заголовки немного отличаются в Windows и Ubuntu. в Windows Content-Type: application/x-www-form-urlencoded Authorization: Basic MjY2MjQyOmFmNDhlZjUwZDFiM2Y5MWRiZmE5ZTAwYTNjM2ZhNjg3 в Ubuntu Content-Type: application/x-www-form-urlencoded Connection: keep-alive Похоже, в Ubuntu отсутствует строка авторизации. Но я нигде не устанавливаю его в Windows. Может ли это иметь какое-то значение?   -  person Aceonace    schedule 08.04.2014
comment
Скорее всего проблема. Эта строка устанавливает авторизацию, и, похоже, это то, что не работает в Ubuntu. req.Credentials = новый NetworkCredential(clientID, apiKey); К сожалению, у меня нет никакого опыта в том, почему это было бы так. Может быть, можно попробовать что-то вроде этого решения. stackoverflow.com/a/19851527/1181408   -  person cgotberg    schedule 08.04.2014
comment
@cgotberg Спасибо за ваш ответ. Действительно, req.Credentials, похоже, не работает с Ubuntu. Следуя вашему опубликованному решению, я добавил заголовок авторизации вручную. Теперь заголовки более или менее идентичны в каждой системе. К сожалению, проблема все еще сохраняется. Я не получаю никаких данных об ответах на Ubuntu. Может ли это быть проблемой сервера, а не программы?   -  person Aceonace    schedule 09.04.2014
comment
Это может быть много вещей, хотя вы могли бы подумать, что идентичные запросы к серверу веб-службы приведут к одинаковым ответам независимо от того, откуда они пришли. Получаете ли вы какой-либо ответ, когда звоните из Ubuntu? Если вы не получите абсолютно никакого подтверждения, это может быть что-то вроде брандмауэра (т. Е. Сервер отказывается отправлять ответ на IP-адрес машины Ubuntu). Если вы получаете ответ, но он пуст, это усложняет решение проблемы.   -  person cgotberg    schedule 09.04.2014
comment
@cgotberg Сначала я подумал, что получаю пустую строку. Хотя после дальнейшего тестирования я теперь убежден, что веб-служба просто не отвечает. В конце концов, это может быть проблема с брандмауэром.   -  person Aceonace    schedule 09.04.2014


Ответы (1)


Наконец-то я нашел, в чем была проблема, и какое-то ее решение. Прежде всего, я написал простой код на Perl, чтобы увидеть, где находится моя проблема: в самом сервере mono или linux.

#!/usr/bin/perl 
use LWP::UserAgent 6;
my $ClientID = "id";
my $APIKey = "key";
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 });
my $req = HTTP::Request->new(GET => 'https://server.url');
$req->authorization_basic($ClientID, $APIKey);
my $res = $ua->request( $req );
print "Status: " . $res->status_line . "\n";
print "Contents: ". $res->content . "\n";

Perl-код работал, давая мне OK в статусе запроса и распечатывая содержимое. Но чтобы это заработало, мне пришлось отключить проверку безопасности сервера LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 }), иначе это не сработало бы. Это заставило меня задуматься. Может быть, поэтому я не получал никаких данных ответа в своем коде C#. Из-за неудачной проверки безопасности. После дальнейшего тестирования я получил подтверждение, что это действительно мой случай. Я не мог понять, почему проверка безопасности ssl не удалась, поэтому я решил полностью проигнорировать проверку, как я сделал в своем коде Perl. Я провел небольшое исследование и нашел эту тему Как игнорировать проверку SSL-сертификата < /а>. Я только что добавил этот фрагмент кода в свою функцию, и он заработал.

System.Net.ServicePointManager.ServerCertificateValidationCallback +=
delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
                    System.Security.Cryptography.X509Certificates.X509Chain chain,
                    System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
    return true; // **** Always accept
};

Я до сих пор не знаю, почему проверка сертификата не работает в Linux. Но по крайней мере, игнорируя проверку, он работает и в Windows, и в Linux. Вот окончательный код:

using System.Net;

static string GetProducts(string clientID, string apiKey, string url)
{
    HttpWebRequest req = WebRequest.Create(url) as HttpWebRequest;
    req.Credentials = new NetworkCredential(clientID, apiKey);
    req.ContentType = "application/x-www-form-urlencoded";

    System.Net.ServicePointManager.ServerCertificateValidationCallback +=
    delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
                    System.Security.Cryptography.X509Certificates.X509Chain chain,
                    System.Net.Security.SslPolicyErrors sslPolicyErrors)
    {
        return true;
    };

    string result = string.Empty;

    using (HttpWebResponse resp = req.GetResponse() as HttpWebResponse)
    {
        StreamReader reader = new StreamReader(resp.GetResponseStream());
        result = reader.ReadToEnd();
    }

    return result;
}
person Aceonace    schedule 11.05.2014