Статьи

Как использовать новый Apache Http Client для отправки HEAD-запроса

Если вы обновили свой код HTTP-клиента Apache для использования новейшей библиотеки (на момент написания этой статьи это версия 4.3.5 для httpclient и версия 4.3.2 для httpcore) с версии 4.2.x, вы заметите, что некоторые классы, такие как org.apache.http.impl.client.DefaultHttpClient или org.apache.http.params.HttpParams , устарели. Ну, я был там, поэтому в этом посте я расскажу, как избавиться от предупреждений с помощью новых классов.

1. Вариант использования с Podcastpedia.org

Вариант использования, который я буду использовать для демонстрации, прост: у меня есть пакетное задание, чтобы проверить, есть ли новые эпизоды, доступные для подкастов. Чтобы избежать необходимости получать и анализировать ленту eTag если новых эпизодов нет, я проверяю ранее, eTag ли eTag или last-modified заголовки ресурса ленты с момента последнего вызова. Это будет работать, если издатель каналов поддерживает эти заголовки, что я настоятельно рекомендую, так как оно экономит полосу пропускания и вычислительную мощность для потребителей.

Так как это работает? Первоначально, когда новый подкаст добавляется в каталог Podcastpedia.org, я проверяю, присутствуют ли заголовки для ресурса канала, и если да, то я сохраняю их в базе данных. Для этого я выполняю HTTP-запрос HEAD к URL-адресу канала с помощью Apache Http Client. Согласно протоколу передачи гипертекста — HTTP / 1.1 rfc2616 , метаинформация, содержащаяся в заголовках HTTP в ответ на запрос HEAD, ДОЛЖНА быть идентична информации, отправленной в ответ на запрос GET).

В следующих разделах я представлю, как на самом деле выглядит код в Java до и после обновления до версии 4.3.x Apache Http Client.

2. Переход на версию 4.3.x

2.1. Программные зависимости

Для создания своего проекта, который, кстати, теперь доступен на GitHub — Podcastpedia-batch , я использую maven, поэтому я перечислил ниже зависимости, необходимые для Apache Http Client:

2.1.1. Перед

Apache Http Клиентские зависимости 4.2.x

01
02
03
04
05
06
07
08
09
10
11
<!-- Apache Http client -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.2.5</version>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore</artifactId>
    <version>4.2.4</version>
</dependency>

2.1.2. После

Apache Http Client зависимости

01
02
03
04
05
06
07
08
09
10
11
<!-- Apache Http client -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.3.5</version>
</dependency>    
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore</artifactId>
    <version>4.3.2</version>
</dependency>

2.2. HEAD-запрос с Apache Http Client

2.2.1. До v4.2.x

Пример выполнения запроса HEAD с помощью Apache HttpClient

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private void setHeaderFieldAttributes(Podcast podcast) throws ClientProtocolException, IOException, DateParseException{
     
    HttpHead headMethod = null;                
    headMethod = new HttpHead(podcast.getUrl());
     
    org.apache.http.client.HttpClient httpClient = new DefaultHttpClient(poolingClientConnectionManager);
     
    HttpParams params = httpClient.getParams();
    org.apache.http.params.HttpConnectionParams.setConnectionTimeout(params, 10000);
    org.apache.http.params.HttpConnectionParams.setSoTimeout(params, 10000);
    HttpResponse httpResponse = httpClient.execute(headMethod);
    int statusCode = httpResponse.getStatusLine().getStatusCode();
   
    if (statusCode != HttpStatus.SC_OK) {
        LOG.error("The introduced URL is not valid " + podcast.getUrl()  + " : " + statusCode);
    }
   
    //set the new etag if existent
    org.apache.http.Header eTagHeader = httpResponse.getLastHeader("etag");
    if(eTagHeader != null){
        podcast.setEtagHeaderField(eTagHeader.getValue());
    }
   
    //set the new "last modified" header field if existent
    org.apache.http.Header lastModifiedHeader= httpResponse.getLastHeader("last-modified");
    if(lastModifiedHeader != null) {
        podcast.setLastModifiedHeaderField(DateUtil.parseDate(lastModifiedHeader.getValue()));
        podcast.setLastModifiedHeaderFieldStr(lastModifiedHeader.getValue());
    }                                                            
 
    // Release the connection.
    headMethod.releaseConnection();                    
}

Если вы используете интеллектуальную среду IDE, она сообщит вам, что DefaultHttpClient , HttpParams и HttpConnectionParams устарели. Если вы посмотрите сейчас в их документах java, вы получите предложение об их замене, а именно, вместо этого использовать HttpClientBuilder и классы, предоставляемые org.apache.http.config .

Итак, как вы увидите в следующем разделе, именно это я и сделал.

2.2.2. После v 4.3.x

Пример запроса HEAD с Apache Http Client v 4.3.x

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private void setHeaderFieldAttributes(Podcast podcast) throws ClientProtocolException, IOException, DateParseException{
     
    HttpHead headMethod = null;                
    headMethod = new HttpHead(podcast.getUrl());
                     
    RequestConfig requestConfig = RequestConfig.custom()
            .setSocketTimeout(TIMEOUT * 1000)
            .setConnectTimeout(TIMEOUT * 1000)
            .build();
     
    CloseableHttpClient httpClient = HttpClientBuilder
                                .create()
                                .setDefaultRequestConfig(requestConfig)
                                .setConnectionManager(poolingHttpClientConnectionManager)
                                .build();
 
    HttpResponse httpResponse = httpClient.execute(headMethod);
    int statusCode = httpResponse.getStatusLine().getStatusCode();
   
    if (statusCode != HttpStatus.SC_OK) {
        LOG.error("The introduced URL is not valid " + podcast.getUrl()  + " : " + statusCode);
    }
   
    //set the new etag if existent
    Header eTagHeader = httpResponse.getLastHeader("etag");
    if(eTagHeader != null){
        podcast.setEtagHeaderField(eTagHeader.getValue());
    }
   
    //set the new "last modified" header field if existent
    Header lastModifiedHeader= httpResponse.getLastHeader("last-modified");
    if(lastModifiedHeader != null) {
        podcast.setLastModifiedHeaderField(DateUtil.parseDate(lastModifiedHeader.getValue()));
        podcast.setLastModifiedHeaderFieldStr(lastModifiedHeader.getValue());
    }                                                            
 
    // Release the connection.
    headMethod.releaseConnection();                    
}

Примечание:

  • как HttpClientBuilder использовался для создания ClosableHttpClient [строки 11-15], который является базовой реализацией HttpClient который также реализует Closeable
  • HttpParams из предыдущей версии были заменены на org.apache.http.client.config.RequestConfig [строки 6-9], где я могу установить время ожидания сокета и соединения. Эта конфигурация позже используется (строка 13) при сборке HttpClient

Оставшаяся часть кода довольно проста:

  • запрос HEAD выполняется (строка 17)
  • если он существует, то eTag и last-modified сохраняются.
  • в конце внутреннее состояние запроса сбрасывается, делая его многоразовым — headMethod.releaseConnection()

2.2.3. Сделайте http-вызов из-за прокси

Если вы находитесь за прокси-сервером, вы можете легко настроить HTTP-вызов, установив прокси-сервер org.apache.http.HttpHost в RequestConfig :

HTTP-вызов за прокси

1
2
3
4
5
6
HttpHost proxy = new HttpHost("xx.xx.xx.xx", 8080, "http");       
RequestConfig requestConfig = RequestConfig.custom()
        .setSocketTimeout(TIMEOUT * 1000)
        .setConnectTimeout(TIMEOUT * 1000)
        .setProxy(proxy)
        .build();

Ресурсы

Исходный код — GitHub

  • podcastpedia-batch — задание на добавление новых подкастов из файла в каталог подкастов, использует код, представленный в посте, для сохранения заголовков eTag и lastModified; это все еще в стадии разработки. Пожалуйста, сделайте запрос, если у вас есть предложения по улучшению

Web