Pull to refresh

xNet — C# библиотека для работы с Web

Reading time 7 min
Views 144K
Постепенно, с изучением C# и .NET Framework, я начал писать различные Helper'ы, которые скрывали рутинный код за вызовом всего одного метода. После это переросло в разработку полноценной библиотеки, которую я хочу вам представить. Данная библиотека написана полностью 'с нуля'.

Так что же такое xNet?


xNet — это библиотека классов для .NET Framework, которая включает в себя:
  • Классы для работы с прокси-серверами: HTTP, Socks4(a), Socks5, Chain.
  • Классы для работы с HTTP 1.0/1.1 протоколом: keep-alive, gzip, deflate, chunked, SSL, прокси и другое.


Скачать: releases
Исходники: github.com/X-rus/xNet

Статья была значительно переписана в связи с выходом новой версии библиотеки. Последнее обновление 12.09.2015

Основы


Для отправки запросов используется HttpRequest. В нём можно задать различные настройки: заголовки, время ожидания, нужно ли следовать переадресации, следует ли держать постоянное соединение и другие. Когда вы отправляете запрос, то он сначала принимает заголовки ответа с помощью HttpResponse, а после возвращает ссылку на этот объект, чтобы вы могли загрузить тело сообщения. Загрузить тело сообщения можно с помощью одного из специальных методов.

Пример запроса:
using (var request = new HttpRequest())
{
    request.UserAgent = Http.ChromeUserAgent();

    // Отправляем запрос.
    HttpResponse response = request.Get("habrahabr.ru");
    // Принимаем тело сообщения в виде строки.
    string content = response.ToString();
}

Оператор using используется для закрытия соединения с сервером. Тоже самое можно сделать вызвав метод HttpRequest.Close.

Получить ссылку на HttpResponse можно также с помощью свойства HttpRequest.Response. Возвращаемый HttpResponse не является уникальным. Для каждого HttpRequest существует только один HttpResponse (он используется повторно при каждом запросе).

Вот так выглядит данный запрос
GET / HTTP/1.1
Host: habrahabr.ru
Connection: keep-alive
Accept-Encoding: gzip,deflate
User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/24.0.1309.0 Safari/537.17


Отправка простых запросов и загрузка тела сообщения


Пример GET-запроса:
using (var request = new HttpRequest())
{
    var urlParams = new RequestParams();

    urlParams["param1"] = "val1";
    urlParams["param2"] = "val2";

    string content = request.Get("habrahabr.ru", urlParams).ToString();
}

RequestParams используется для установки параметров URL-адреса. В данном случае, это равносильно следующей записи:
Get("habrahabr.ru/?param1=val1&param2=val2")


Задать параметры URL-адреса можно с помощью метода HttpRequest.AddUrlParam. Эти параметры будут стёрты после первого же запроса.

Пример GET-запроса с временными параметрами URL-адреса:
using (var request = new HttpRequest())
{
    request.AddUrlParam("param1", "val1").AddUrlParam("param2", "val2");

    string content = request.Get("habrahabr.ru").ToString();
}


Пример POST-запроса:
using (var request = new HttpRequest())
{
    var reqParams = new RequestParams();

    reqParams["login"] = "neo";
    reqParams["password"] = "knockknock";

    string content = request.Post(
        "www.whitehouse.gov", reqParams).ToString();
}


Задать параметры запроса можно с помощью HttpRequest.AddParam. Эти параметры будут стёрты после первого же запроса.

Пример POST-запроса с временными параметрами:
using (var request = new HttpRequest())
{
    request.AddParam("login", "neo").AddParam("password", "knockknock");

    string content = request.Post("www.whitehouse.gov").ToString();
}


Также вы можете отправить строку, массив байтов, файл или любой объект наследующий от HttpContent.

Для загрузки тела сообщения используется один из пяти методов HttpResponse: ToString, ToBytes, ToFile, ToStream или ToMemoryStream. Если тело сообщения не нужно, то следует вызвать метод None.

Если вы хотите, чтобы параметры запроса кодировались, допустим, в 1251 кодировке, то нужно задать свойство HttpRequest.CharacterSet = Encoding.GetEncoding(1251)

Отправка Multipart/form данных


О Multipart/form данных. Для работы с ними используется класс MultipartContent.

Пример Multipart/form запроса:
using (var request = new HttpRequest())
{
    var multipartContent = new MultipartContent()
    {
        {new StringContent("Bill Gates"), "login"},
        {new StringContent("qwerthahaha"), "password"},
        {new FileContent(@"C:\windows_9_alpha.rar"), "file1", "1.rar"}
    };

    request.Post("www.microsoft.com", multipartContent).None();
}


Чтобы не создавать самим объект класса MultipartContent, можно использовать два специальных метода HttpRequest: AddField и AddFile. Эти параметры будут стёрты после первого же запроса.

Пример Multipart/form запроса с временными параметрами:
using (var request = new HttpRequest())
{
    request
        .AddField("login", "Bill Gates")
        .AddField("password", "qwerthahaha")
        .AddFile("file1", @"C:\windows_9_alpha.rar");

    request.Post("www.microsoft.com").None();
}


Постоянное соединение и переподключение


HttpRequest поддерживает постоянные соединения. Их можно отключить при помощи свойства HttpRequest.KeepAlive.

Пример:
using (var request = new HttpRequest("habrahabr.ru"))
{
    request.Get("/").None();
    request.Get("/feed").None();
    request.Get("/feed/posts");
}

Все три запроса будут выполнены с помощью одного и того же соединения, но это только в том случае, если сервер не закроет соединение раньше. Обратите внимание — в последнем запросе я не вызываю метод None. Дело в том, что он загружает тело сообщения, чтобы следующий запрос мог корректно выполниться при использовании постоянного соединения, а так как это последний запрос, то можно не загружать тело сообщения, ведь соединение все равно будет закрыто.

Ещё немного о методе None. В случае если постоянное соединение не используется, то данный метод просто закрывает соединение. Если вы не вызовете данный метод, то он будет вызван автоматически при следующем запросе. Я рекомендую самим вызывать данный метод.

Имейте в виду, при работе с постоянными соединениями могут происходить отключения от сервера. Это может произойти по причине того, что на сервере достигнут предел запросов для одного соединения, либо вышло время ожидания следующего запроса. HttpRequest умеет обрабатывать такие ситуации, пробуя подключиться заново (это поведение не связанно с Reconnect). Если подключиться не удастся, то он выдаст исключение. Для более тонкой настройки постоянных соединений можно использовать свойства KeepAliveTimeout и MaximumKeepAliveRequests.

Дополнительно вы можете управлять переподключениями при помощи свойств Reconnect, ReconnectLimit и ReconnectDelay. По умолчанию это поведение отключено. HttpRequest будет пробовать переподключиться в том случае, если произойдёт ошибка во время подключения к серверу, либо если произойдёт ошибка во время загрузки/отправки данных. Но переподключение не сработает, если ошибка произойдёт во время загрузки тела сообщения (в методе ToString(), например). Это поведение может пригодиться при работе с прокси или если у вас нестабильное соединение с интернетом.

Установка куки, заголовков и другого


Установка дополнительных заголовков выполняется с помощью специального индексатора. Имейте в виду, некоторые заголовки можно задавать только с помощью специальных свойств. Их список можно найти в документации. Заголовки заданные через индексатор отправляются во всех запросах. Если вам нужно установить временный заголовок для одного запроса, то используйте метод HttpRequest.AddHeader. Такие заголовки перекрывают заголовки заданные через индексатор.

Куки задаются с помощью свойства HttpRequest.Cookies. Куки могут изменяться в зависимости от ответа сервера. Чтобы не допустить этого, нужно установить значение свойства CookieDictionary.IsLocked равным true.

Пример:
using (var request = new HttpRequest("habrahabr.ru"))
{
    request.Cookies = new CookieDictionary()
    {
        {"hash", "yrttsumi"},
        {"super-hash", "df56ghd"}
    };

    request[HttpHeader.DNT] = "1";
    request["X-Secret-Param"] = "UFO";

    request.AddHeader("X-Tmp-Secret-Param", "42");
    request.AddHeader(HttpHeader.Referer, "http://site.com");

    request.Get("/");
}


У HttpRequest и у HttpResponse есть дополнительные методы для работы с куки и заголовками: ContainsHeader, EnumerateHeaders, ClearAllHeaders, ContainsCookie, ContainsRawCookie и другие.

Для отправки запросов нужно задать User-Agent. Его можно сгенерировать одним из методов Http: IEUserAgent, OperaUserAgent, ChromeUserAgent, FirefoxUserAgent или OperaMiniUserAgent.
И задать так:
request.UserAgent = Http.ChromeUserAgent();


Раньше существовал метод RandomUserAgent, но я его удалил, так как его использование может привести к некоторым проблемам. Допустим, сайт для Google Chrome выдаёт один контент, а для IE уже немного другой, или совсем другой. Поэтому, с осторожностью используйте User Agent'ты от различных браузеров.

У HttpRequest есть ещё много различных свойств, с помощью которых вы можете манипулировать отправляемыми заголовками. Смотрите документацию.

Соединение через прокси-сервер


xNet поддерживает работу со следующими типами прокси-серверов:
  • HTTP — класс HttpProxyClient
  • Socks4(a) — класс Socks4ProxyClient(a)
  • Socks5 — класс Socks5ProxyClient

Все эти классы наследуют от класса ProxyClient. Есть дополнительный класс ChainProxyClient, который позволяет создавать соединение через цепочку прокси-серверов.

Пример:
var proxyClient = HttpProxyClient.Parse("127.0.0.1:8080");
var tcpClient = proxyClient.CreateConnection("habrahabr.ru", 80);
// Далее работаем с соединением.


HttpRequest поддерживает работу через прокси-сервер. Для этого нужно задать свойство HttpRequest.Proxy.

Обработка ошибок


Если при работе с HTTP-протоколом произойдёт ошибка, то будет возбуждено исключение HttpException, а если произойдёт ошибка при работе с прокси-сервером, то будет возбуждено исключение ProxyException. Оба этих исключения наследуют от класса NetException.

Пример:
try
{
    using (var request = new HttpRequest())
    {
        request.Proxy = Socks5ProxyClient.Parse("127.0.0.1:1080");
        request.Get("habrahabr.ru");
    }
}
catch (HttpException ex)
{
    Console.WriteLine("Произошла ошибка при работе с HTTP-сервером: {0}", ex.Message);

    switch (ex.Status)
    {
        case HttpExceptionStatus.Other:
            Console.WriteLine("Неизвестная ошибка");
            break;

        case HttpExceptionStatus.ProtocolError:
            Console.WriteLine("Код состояния: {0}", (int)ex.HttpStatusCode);
            break;

        case HttpExceptionStatus.ConnectFailure:
            Console.WriteLine("Не удалось соединиться с HTTP-сервером.");
            break;

        case HttpExceptionStatus.SendFailure:
            Console.WriteLine("Не удалось отправить запрос HTTP-серверу.");
            break;

        case HttpExceptionStatus.ReceiveFailure:
            Console.WriteLine("Не удалось загрузить ответ от HTTP-сервера.");
            break;
    }
}


Дополнительные классы


Класс WinInet позволяет взаимодействовать с настройками сети операционной системы Windows. С помощью него можно узнать, подключён ли компьютер к интернету или получить значение прокси-сервера Internet Explorer'а.

Класс Html предназначен для помощи в работе с HTML и с другими текстовыми данными. В нём находится метод, который заменяет в тексте HTML-сущности на представляющие их символы. Пример: &_quot;test&_quot; в «test». Метод, который заменяет Unicode-сущности на представляющие их символы. Пример: \u2320 или \U044F. И методы для извлечения строк: Substring, LastSubstring, Substrings.

Ссылки по теме


Tags:
Hubs:
+25
Comments 42
Comments Comments 42

Articles