Готовый перевод HttpClient 4.x (Tutorial) / HttpClient 4.x (Учебник): 1. Fundamentals (Основы)

Внимание! Этот перевод, возможно, ещё не готов, так как модераторы установили для него статус «идёт перевод»

Глава 1. Основы

1.1. Выполнение запроса

Наиболее важной функцией HttpClient является выполнение HTTP-методов. Выполнение HTTP-метода включает в себя один или несколько обменов HTTP-запросов/HTTP-ответ, как правило, обрабатываемых внутри HttpClient. Пользователь, как ожидается, предоставляет объект запроса на выполнение экземплязу HttpClient, который, в свою очередь, передает запрос на целевой сервер и возвращает соответствующий объект ответа, или выбрасывает исключение, если выполнение не удалось.

Вполне естественно, что основной точкой входа в HttpClient API является интерфейс HttpClient, который определяет акт, описанный выше.

Вот пример процесса выполнения запроса в своей простейшей форме:

HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null)
{
InputStream instream = entity.getContent();
int l;
byte[] tmp = new byte[2048];
while ((l = instream.read(tmp)) != -1)
{
}
}

1.1.1. HTTP-запрос

Все HTTP запросы имеют строку запроса, состоящую из имени метода, запрашиваемого URI и версии HTTP протокола.

HttpClient поддерживает все HTTP-методы, определенные в спецификации HTTP/1.1: GET, HEAD, POST, PUT, DELETE, TRACE и OPTIONS. Существует специальный класс для каждого типа метода: HttpGet, HttpHead, HttpPost, HttpPut, HttpDelete, HttpTrace, и HttpOptions.

Request-URI - это Универсальным Идентификатором Ресурса, который идентифицирует ресурс, к которому обращен запрос. URI HTTP-запроса состоит из схемы протокола, имени хоста, дополнительно порта, пути к ресурсу, дополнительно запроса, и дополнительный фрагмента.

HttpGet httpget = new HttpGet( "http://www.google.com/search?hl=en&q=httpclient&btnG=Google+Search&aq=f&oq=");

HttpClient предоставляет ряд полезных методов для упрощения создания и модификации запрашиваемых URI.

URI может быть составлен програмно:

URI uri = URIUtils.createURI("http", "www.google.com", -1, "/search", "q=httpclient&btnG=Google+Search&aq=f&oq=", null);
HttpGet httpget = new HttpGet(uri);
System.out.println(httpget.getURI());

stdout >

http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq=

Строка запроса также может быть создана из индивидуальных параметров:

List<namevaluepair> qparams = new ArrayList<namevaluepair>();
qparams.add(new BasicNameValuePair("q", "httpclient"));
qparams.add(new BasicNameValuePair("btnG", "Google Search")); qparams.add(new BasicNameValuePair("aq", "f"));
qparams.add(new BasicNameValuePair("oq", null));
URI uri = URIUtils.createURI("http", "www.google.com", -1, "/search", URLEncodedUtils.format(qparams, "UTF-8"), null);
HttpGet httpget = new HttpGet(uri);
System.out.println(httpget.getURI());</namevaluepair></namevaluepair>

stdout >

http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq=

1.1.2. HTTP-ответ

HTTP ответ - это сообщение, отправленное сервером клиенту после получения и интерпретации сообщения запроса. Первая строка этого сообщения состоит из версии протокола, за которой следует цифровой код статуса и связанная с ним текстовая фраза.

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");

System.out.println(response.getProtocolVersion());
System.out.println(response.getStatusLine().getStatusCode());
System.out.println(response.getStatusLine().getReasonPhrase());
System.out.println(response.getStatusLine().toString())

stdout >

HTTP/1.1
200
OK
HTTP/1.1 200 OK

1.1.3. Заголовки HTTP-сообщения

HTTP сообщение может содержать ряд заголовков, описывающих свойства сообщения, такие как размер контента, тип содержания, и т.д. HttpClient предоставляет методы для извлечения, добавления, удаления и перечисления заголовков.

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie", "c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie", "c2=b; path=\"/\", c3=c; domain=\"localhost\"");
Header h1 = response.getFirstHeader("Set-Cookie");
System.out.println(h1); Header h2 = response.getLastHeader("Set-Cookie"); System.out.println(h2);
Header[] hs = response.getHeaders("Set-Cookie");
System.out.println(hs.length);

stdout >

Set-Cookie: c1=a; path=/; domain=localhost Set-Cookie: c2=b; path="/", c3=c; domain="localhost" 2

Наиболее эффективным способ получить все заголовки конкретного типа - использование интерфейса HeaderIterator.

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie", "c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie", "c2=b; path=\"/\", c3=c; domain=\"localhost\"");

HeaderIterator it = response.headerIterator("Set-Cookie");

while (it.hasNext())
{
System.out.println(it.next());
}

stdout >

Set-Cookie: c1=a; path=/; domain=localhost Set-Cookie: c2=b; path="/", c3=c; domain="localhost"

Он также предоставляет удобные методы для разбора HTTP-сообщений на отдельных элементы заголовка.

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); response.addHeader("Set-Cookie", "c1=a; path=/; domain=localhost"); response.addHeader("Set-Cookie", "c2=b; path=\"/\", c3=c; domain=\"localhost\"");

HeaderElementIterator it = new BasicHeaderElementIterator( response.headerIterator("Set-Cookie"));

while (it.hasNext())
{
HeaderElement elem = it.nextElement();
System.out.println(elem.getName() + " = " + elem.getValue());
NameValuePair[] params = elem.getParameters();
for (int i = 0; i &lt; params.length; i++)
{
System.out.println(" " + params[i]);
}
}

stdout >

c1 = a path=/ domain=localhost c2 = b path=/ c3 = c domain=localhost

1.1.4. HTTP-объект

HTTP сообщения могут передавать объекты содержания, ассоциированные с запросом или ответом. Объекты содержатся не во всех запросах и ответах, так как являются не обязательными. Запросы, использующие объекты, называются запросами, включающими объекты (entity enclosing requests). Спецификация HTTP определяет два методы для работы с включенными объектами: POST и PUT. Ответы, как правило, включают объект содержания. Однако, из этого правила есть исключения, напримет ответы на HEAD-метод и 204 No Content, 304 Not Modified, 205 Reset Content responses.

HttpClient выделяет три вида объектов, в зависимости от того, откуда получено содержание:

Потоковые объекты: Содержание, полученно из потока, либо сгенерировано на лету. В частности, к этой категории относятся объекты, полученные из HTTP-ответов. Поточные объекты, как правило, не являются повторяемыми.

Автономные объекты: Содержание, находится в памяти или полученно независимо от соединения или других объектов. Автономные объекты, как правило, являются повторяемыми. Этот тип объектов в основном используется для объектов, входящих в HTTP-запросы.

Объекты – оболочки: Содержание, полученно из других объектов.

Это различие имеет важное значение для управления соединениями, когда содержимое HTTP-ответов передается в виде потока. Для объектов HTTP-запросов, которые создаются приложением и лишь отправляются с помощью экземпляра HttpClient, разница между потоковыми и автономными объектами не имеет большого значения. В этом случае, предлагается рассмотривать неповторяющиеся объекты как потоковые, а повторяющиеся - как автономные.

1.1.4.1. Повторяемые объекты
Компонент может быть повторяемым, то есть его содержание может быть прочитано несколько раз. Это возможно только для автономным компонентов (как ByteArrayEntity или StringEntity)

1.1.4.2. Использование HTTP-объектов

Так как объекты могут представлять как бинарное, так и символьное содержание, они имеют поддержку кодировки символов (для поддержки последнего, т.е. символьного содержания).

Объект создается при выполнении запроса с включенным содержанием или когда запрос была успешной и тело ответа используется для отправки результата клиенту.

Чтобы прочитать содержимое объекта, можно либо получить входной поток с помощью метода HttpEntity.GetContent(), который возвращает java.io.InputStream, либо можно передать выходной поток в метод HttpEntity.writeTo(OutputStream), который вернет сразу все содержимое, которое было записано в данный поток.

Как только объект был получен из входящего сообщения, методы HttpEntity.getContentType() и HttpEntity.getContentLength() могут быть использованы для чтения общих метаданных, такие как Content-Type и Content-Length заголовков (если они доступны). Content-Type заголовок может включать информацию о кодировке текстовых MIME-типов, таких как text/plain или text/html; для чтения этой информации используется метод HttpEntity.getContentEncoding(). Если заголовки отсутствуют, то для длины возвращается -1, а для типа содержимого NULL. Если же Content-Type заголовок доступен, то будет возвращен экземпляр Header.

Когда объект создается для исходящего сообщения, эти мета данные должны быть установлены создателем объекта.

StringEntity myEntity = new StringEntity("important message", "UTF-8");

System.out.println(myEntity.getContentType()); System.out.println(myEntity.getContentLength()); System.out.println(EntityUtils.getContentCharSet(myEntity)); System.out.println(EntityUtils.toString(myEntity)); System.out.println(EntityUtils.toByteArray(myEntity).length);

stdout >

Content-Type: text/plain; charset=UTF-8 17 UTF-8 important message 17

1.1.5. Освобождение низкоуровневых ресурсов

Закончив работу с объектом ответа, важно обеспечить, чтобы содержимое объекта было извлечено полностью, после чего соединение может быть благополучно возвращено в пул соединений и затем повторно использовано диспетчером соединений для последующих запросов. Самый простой способ сделать это - вызвать метод HttpEntity.consumeContent() для извлечения из потока имеющегося содержания. При этом HttpClient автоматически возвращает лежащее в основе соединение обратно в Connection Manager, как только он обнаруживает, что был достигнут конец содержимого потока. Метод HttpEntity.consumeContent() является безопасным для вызова больше чем один раз.

Однако, может возникнуть ситуация, когда нужно извлечь лишь небольшую часть из всего содержимого ответа а потери производительности при извлечении оставшейся части содержания и освобождении соединения для многоразового использования слишком высоки; в таком случае можно просто прервать запрос, вызвав метод HttpUriRequest.abort().

HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null)
{
InputStream instream = entity.getContent();
int byteOne = instream.read();
int byteTwo = instream.read();
// Do not need the rest
httpget.abort();
}

Соединение не будет использовано повторно, но ресурсы всех уровней, занятые им, будут правильно освобождены.

1.1.6. Извлечение содержания объектов

Рекомендуется извлекать содержимое объектов, используя их методы HttpEntity.GetContent() или HttpEntity.writeTo (OutputStream). HttpClient также поставляется с классом EntityUtils, который предоставляет несколько статических методов для более легкого чтения содержания или информации об объекте. Вместо непосредственного чтения java.io.InputStream, можно получить все тело содержание в виде строки/массива, используя методы этого класса. Однако, использование EntityUtils настоятельно не рекомендуется, если ответы, получены от надежных серверов HTTP и, как известно, ограниченной длины.???

HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null)
{
long len = entity.getContentLength();
if (len != -1 &amp;&amp; len &lt; 2048)
{
System.out.println(EntityUtils.toString(entity));
}
else
{
// Stream content out
}
}

В некоторых ситуациях может потребоваться читать содержание объекта более одного раза. В этом случае содержание этого объекта должно быть буферизовано с помощью буфера в памяти или на диске. Простейший способ сделать это - обернуть оригинальный объект с помощью класса BufferedHttpEntity. Это приведет к тому, что оригинальное содержание объекта будет считываться в буфер в памяти. Во других случаях оболочка объекта будет иметь оригинал.

HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null)
{
entity = new BufferedHttpEntity(entity);
}

1.1.7. Конструирование содержания объекта

HttpClient предоставляет несколько классов, которые могут быть использованы для эффективного извлечения потока содержания из HTTP-соединений. Экземпляры этих классов могут быть связаны с запросами, содержащими объекты, такими как POST и PUT, чтобы заключить содержимое объектов в исходящие HTTP-запросы. HttpClient предоставляет несколько классов для наиболее распространенных контейнеров данных, таких как строка, массив, входной поток, и файл: StringEntity, ByteArrayEntity, InputStreamEntity, и FileEntity.

File file = new File("somefile.txt");
FileEntity entity = new FileEntity(file, "text/plain; charset=\"UTF-8\"");

HttpPost httppost = new HttpPost("http://localhost/action.do"); httppost.setEntity(entity);

Пожалуйста, обратите внимание,что InputStreamEntity не является повторяемым, потому что он может быть прочитан из основного потока данных только один раз. Вообще рекомендуется для реализации пользовательского класса использовать HttpEntity, который является самодостаточным, а не общий InputStreamEntity. FileEntity может быть хорошей отправной точкой.
1.1.7.1. Объекты с динамическим содержанием

Часто HTTP-объекты должны быть сгенерированы динамически, на основе частично выполненного контекста. HttpClient обеспечивает поддержку динамических объектов с помощью класса EntityTemplate и интерфейса ContentProducer. Экземпляры ContentProducer являются объектами, которые генерируют свое содержание по требованию, записывая его в выходной поток. Они должны быть в состоянии генерировать содержание каждый раз, когда от них это требуется. Так объекты, созданные с помощью EntityTemplate, как правило, самодостаточны и являются повторяемыми.

ContentProducer cp = new ContentProducer()
{
public void writeTo(OutputStream outstream) throws IOException
{
Writer writer = new OutputStreamWriter(outstream, "UTF-8");
writer.write("<response>"); writer.write(" <content>");
writer.write(" important stuff"); writer.write(" </content>");
writer.write("</response>"); writer.flush();
}
};
HttpEntity entity = new EntityTemplate(cp); HttpPost httppost = new HttpPost("http://localhost/handler.do");
httppost.setEntity(entity);

1.1.7.2. HTML-формы

Многим приложениям часто необходимо имитировать процесс заполнения HTML-форм, например, для того, чтобы войти в веб-приложение или предоставить входные данные. HttpClient предоставляет специальный объектный класс UrlEncodedFormEntity для облегчения подобных действий.

List<namevaluepair> formparams = new ArrayList<namevaluepair>();
formparams.add(new BasicNameValuePair("param1", "value1"));
formparams.add(new BasicNameValuePair("param2", "value2"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, "UTF-8");
HttpPost httppost = new HttpPost("http://localhost/handler.do");
httppost.setEntity(entity);</namevaluepair></namevaluepair>

Этот экземпляр UrlEncodedFormEntity будет использовать так называемое URL-кодирование для кодирования параметров и сгенерирует следующие данные:

param1=value1&param2=value2

1.1.7.3.

Вообще рекомендуется, чтобы HttpClient выбирал наиболее подходящую для передачи кодировку на основе свойств переданного HTTP-сообщения. Не исключено, однако, сообщить, что HttpClient кусок кодирования является предпочтительным путем установки HttpEntity.setChunked(), чтобы правда. Обратите внимание, что HttpClient будет использовать этот флаг только в качестве подсказки. Это значение также будет проигнорировано при использовании версий HTTP-протокола, которые не поддерживают блок кодирования, такие как HTTP/1.0.

StringEntity entity = new StringEntity("important message", "text/plain; charset=\"UTF-8\"");
entity.setChunked(true);
HttpPost httppost = new HttpPost("http://localhost/acrtion.do");
httppost.setEntity(entity);

1.1.8. Обработчики ответа

Самый простой и самый удобный способ для обработки ответов - с помощью интерфейса ResponseHandler. Этот метод полностью избавляет пользователя от необходимости беспокоиться об управлении соединением. При использовании ResponseHandler HttpClient автоматически позаботится обеспечения выпуска обратной связи на связи независимо от того, менеджер выполнения запроса успешно или причины исключения.

HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet("http://localhost/");

ResponseHandler handler = new ResponseHandler() { public byte[] handleResponse( HttpResponse response) throws ClientProtocolException, IOException
{
HttpEntity entity = response.getEntity();
if (entity != null)
{
return EntityUtils.toByteArray(entity);
}
else
{
return null;
}
}
};

byte[] response = httpclient.execute(httpget, handler);

1.2. HTTP-контекст выполнения

Первоначально HTTP был разработан без поддежрки состояний, как ответ-запрос ориентированный протокол. Однако, в реальных приложениях часто требуется иметь возможность сохранять информацию о состоянии после нескольких логически связанных обменов запрос-ответ. Для того, чтобы включить в приложение поддержку обработку HttpClient состояние позволяет HTTP запросы выполняются в течение определенного контекста выполнения, называется контексте HTTP. Несколько логически связанные запросы могут участвовать в логической сессии, если же контексте повторно между последовательными запросов. функции HTTP контексте аналогично java.util.Map . Это просто набор произвольных именованых значений. Приложение может заполнить атрибуты контекста до выполнения запроса или проанализировать контекст после исполнения запроса.

В ходе выполнения HTTP-запроса HttpClient добавляет следующие атрибуты контекста исполнения:

'http.connection': экземпляр HttpConnection, представляющий фактическое соединение с целевым хостом.

'http.target_host': экземпляр HttpHost, представляющий целевой хост соединения.

'http.proxy_host': экземпляр HttpHost, представляющий прокси, если он используется.

'http.request': экземпляр HttpRequest, представляющий актуальный HTTP-запрос.

'http.response': экземпляр HttpResponse, представляющий актуальный HTTP-ответ.

'http.request_sent': экземпляр java.lang.Boolean, представляющий флаг, указывающий, был ли запрос передан целевому хосту полностью.

Например, при переадресации, в целях определения окончательного целевого хоста, можно проверить значение атрибута http.target_host после выполнения запроса:

DefaultHttpClient httpclient = new DefaultHttpClient();

HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("http://www.google.com/");

HttpResponse response = httpclient.execute(httpget, localContext);

HttpHost target = (HttpHost) localContext.getAttribute( ExecutionContext.HTTP_TARGET_HOST);

System.out.println("Final target: " + target);

HttpEntity entity = response.getEntity();
if (entity != null)
{
entity.consumeContent();
}

stdout >

Final target: http://www.google.ch

1.3. Обработка исключений

HttpClient может выбросить два типа исключений: java.io.IOException, в случае ошибок ввода/вывода, вызванных таймаутом сокета или сбросом сокета, и HttpException, сигнализирующее об отказе HTTP, вызванное нарушением протокола HTTP. Обычно ошибки ввода-вывода считаются не критичными и подлежат восстановлению, тогда как ошибки HTTP протокола считаются критичными и не могут быть автоматически исправлены.
1.3.1. Безопасность HTTP транспортировки

Важно понимать, что протокол HTTP хорошо подходит не для всех типов приложений. HTTP является протоколом, ориентированным на простые запрос/ответ, который был первоначально разработан для поддержки поиска статического или динамического контента. Он никогда не был предназначен для поддержки транзакций. Например, HTTP-сервер полагает свою часть договора выполненным, если ему удается принять и обработать запрос, сгенерировать ответ и отправить код статуса обратно клиенту. Сервер не будет пытаться откатить транзакции, если клиент не может получить ответ в полном объеме из-за таймаута, отмены запроса или отказа системы. Если клиент решит повторить тот же запрос, то сервер будет неизбежно в конечном итоге выполнения одной транзакции более одного раза. В некоторых случаях это может привести к повреждению данных или неустойчивому состоянию приложения.

Хотя HTTP никогда не была предназначена для поддержки обработки транзакций, он все еще может быть использован в качестве транспортного протокола для критически важных приложений при условии соблюдении определенных условий. Для обеспечения безопасности HTTP транспортном уровне система должна обеспечивать идемпотентности методы HTTP на уровне приложений.

1.3.2. Идемпотентные методы

1.4. Прерывание запросов

DefaultHttpClient httpclient = new DefaultHttpClient();

HttpContext localContext = new BasicHttpContext();

localContext.setAttribute("count", count);

httpclient.addRequestInterceptor(new HttpRequestInterceptor()
{

public void process( final HttpRequest request, final HttpContext context) throws HttpException, IOException
{
AtomicInteger count = (AtomicInteger) context.getAttribute("count");
request.addHeader("Count", Integer.toString(count.getAndIncrement()));
}

});

HttpGet httpget = new HttpGet("http://localhost/"); for (int i = 0; i &lt; 10; i++) { HttpResponse response = httpclient.execute(httpget, localContext);

HttpEntity entity = response.getEntity(); if (entity != null) { entity.consumeContent(); } }

1.6. HTTP-параметры

Интерфейс HttpParams представляет коллекцию неизменяемых значений, которые определяют поведение компонента времени выполнения. Во многих отношениях HttpParams похож на HttpContext. Основное различие между ними заключается в их использовании во время выполнения. Оба интерфейса представляют собой набор объектов, которые организованы в виде карты ключ — значение, но служат разным целям:

Внимание! Этот перевод, возможно, ещё не готов.
Его статус: идёт перевод

Переведено на Нотабеноиде
http://notabenoid.org/book/12990/42234

Переводчики: gi1gi

Настройки

Готово:

69.83% КП = 1.0

Непереведённые фрагменты будут пропущены.