Эта статья будет посвящена быстрому побочному проекту — роботу, который автоматически отправляет в Твиттере топ-вопросы с различных сайтов Q & A StackExchange , таких как StackOverflow , ServerFault , SuperUser и т. Д. Мы создадим простой клиент для API StackExchange, а затем настроим взаимодействие с API Twitter с использованием Spring Social — эта первая часть будет посвящена только клиенту StackExchange. Первоначальная цель этой реализации — не быть полноценным клиентом для всего API StackExchange — это выходит за рамки этого проекта. Единственная причина, по которой существует Клиент, заключается в том, что я не смог найти тот, который работал бы против версии 2.x официального API.
1. Зависимости Maven
Для использования REST API StackExchange нам понадобится очень мало зависимостей — по сути, просто HTTP-клиент — Apache HttpClient прекрасно подойдет для этой цели:
1
2
3
4
5
|
< dependency > < groupId >org.apache.httpcomponents</ groupId > < artifactId >httpclient</ artifactId > < version >4.2.3</ version > </dependency |
Spring RestTemplate также можно было бы использовать для взаимодействия с HTTP API, но это привело бы к большому количеству других связанных с Spring зависимостей в проекте, которые не являются строго необходимыми, поэтому HttpClient сделает все легко и просто.
2. Клиент вопросов
Цель этого клиента — использовать REST-сервис / questions, который публикует StackExchange, а не предоставлять универсальный клиент для всех API-интерфейсов StackExchange — поэтому в этой статье мы рассмотрим только это. Фактическая HTTP-связь с использованием HTTPClient относительно проста:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
public String questions( int min, String questionsUri) { HttpGet request = null ; try { request = new HttpGet(questionsUri); HttpResponse httpResponse = client.execute(request); InputStream entityContentStream = httpResponse.getEntity().getContent(); return IOUtils.toString(entityContentStream, Charset.forName( 'utf-8' )); } catch (IOException ex) { throw new IllegalStateException(ex); } finally { if (request != null ) { request.releaseConnection(); } } } |
Это простое взаимодействие вполне подходит для получения необработанных JSON вопросов, которые публикует API — следующим шагом будет обработка этого JSON. Здесь есть одна важная деталь — это аргумент метода questionsUri — есть несколько API StackExchange, которые могут публиковать вопросы (как предполагает официальная документация ), и этот метод должен быть достаточно гибким, чтобы использовать их все. Например, он может использовать самый простой API-интерфейс, который возвращает вопросы, задав для параметра QuestionUri значение https://api.stackexchange.com/2.1/questions?site=stackoverflow, или он может использовать тег на основе https://api.stackexchange.com/. 2.1 / tags / {tags} / faq? Site = stackoverflow API, в зависимости от того, что нужно клиенту.
Запрос к API StackExchange полностью настроен с параметрами запроса, даже для более сложных запросов расширенного поиска — тело не отправляется. Чтобы построить questionsUri , мы создадим базовый свободный класс RequestBuilder, который будет использовать URIBuilder из библиотеки HttpClient. Это позаботится о правильном кодировании URI и, как правило, о том, что конечный результат верен:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
public class RequestBuilder { private Map<String, Object> parameters = new HashMap<>(); public RequestBuilder add(String paramName, Object paramValue) { this .parameters.put(paramName, paramValue); return this ; } public String build() { URIBuilder uriBuilder = new URIBuilder(); for (Entry<String, Object> param : this .parameters.entrySet()) { uriBuilder.addParameter(param.getKey(), param.getValue().toString()); } return uriBuilder.toString(); } } |
Итак, теперь, чтобы создать действительный URI для API StackExchange:
1
2
3
|
String params = new RequestBuilder(). add( 'order' , 'desc' ).add( 'sort' , 'votes' ).add( 'min' , min).add( 'site' , site).build(); |
3. Тестирование клиента
Клиент выведет сырой JSON, но для проверки нам понадобится библиотека обработки JSON, в частности, Jackson 2 :
1
2
3
4
5
6
|
< dependency > < groupId >com.fasterxml.jackson.core</ groupId > < artifactId >jackson-databind</ artifactId > < version >2.1.3</ version > < scope >test</ scope > </ dependency > |
Тесты, которые мы рассмотрим, будут взаимодействовать с реальным API StackExchange:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
@Test public void whenRequestIsPerformed_thenSuccess() throws ClientProtocolException, IOException { HttpResponse response = questionsApi.questionsAsResponse( 50 , Site.serverfault); assertThat(response.getStatusLine().getStatusCode(), equalTo( 200 )); } @Test public void whenRequestIsPerformed_thenOutputIsJson() throws ClientProtocolException, IOException { HttpResponse response = questionsApi.questionsAsResponse( 50 , Site.serverfault); String contentType = httpResponse.getHeaders(HttpHeaders.CONTENT_TYPE)[ 0 ].getValue(); assertThat(contentType, containsString( 'application/json' )); } @Test public void whenParsingOutputFromQuestionsApi_thenOutputContainsSomeQuestions() throws ClientProtocolException, IOException { String questionsAsJson = questionsApi.questions( 50 , Site.serverfault); JsonNode rootNode = new ObjectMapper().readTree(questionsAsJson); ArrayNode questionsArray = (ArrayNode) rootNode.get( 'items' ); assertThat(questionsArray.size(), greaterThan( 20 )); } |
Первый тест подтвердил, что ответ, предоставленный API, действительно был 200 OK, поэтому запрос GET для получения вопросов был фактически успешным. После того, как это основное условие было обеспечено, мы перешли к представлению — как указано в HTTP-заголовке Content-Type — которое должно быть JSON. Затем мы на самом деле анализируем JSON и проверяем, что в этом выводе действительно есть Вопросы — сама логика синтаксического анализа является низкоуровневой и простой, что достаточно для целей теста. Обратите внимание, что эти запросы учитываются для ваших ограничений скорости, указанных API — по этой причине тесты Live исключаются из стандартной сборки Maven:
01
02
03
04
05
06
07
08
09
10
|
< plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-surefire-plugin</ artifactId > < version >2.13</ version > < configuration > < excludes > < exclude >**/*LiveTest.java</ exclude > </ excludes > </ configuration > </ plugin > |
4. Следующий шаг
Текущий Клиент ориентирован только на один тип Ресурса из множества доступных типов, опубликованных API-интерфейсами StackExchange. Это связано с тем, что его первоначальная цель ограничена — ему нужно только разрешить пользователю использовать Вопросы с различных сайтов в портфеле StackExchange. Следовательно, клиент может быть улучшен за пределами этого первоначального варианта использования, чтобы иметь возможность использовать другие типы API . Реализация также очень сырая — после использования REST-службы вопросов она просто возвращает вывод JSON в виде строки, а не какую-либо модель вопросов из этого вывода. Таким образом, потенциальным следующим шагом было бы демонтировать этот JSON в правильный домен DTO и вернуть его обратно вместо необработанного JSON.
5. Заключение
Целью этой статьи было показать, как начать создание интеграции с API StackExchange или действительно с API на основе HTTP. В нем рассказывалось о том, как писать интеграционные тесты для реального API и убедиться, что сквозное взаимодействие действительно работает.
Во второй части этой статьи будет показано, как взаимодействовать с API-интерфейсом Twitter с помощью библиотеки Spring Social, и как использовать клиент StackExchange, который мы создали здесь, чтобы твитить вопросы в новой учетной записи Twitter.
Я уже настроил несколько учетных записей в Твиттере, которые теперь пишут в Твиттере 2 главных вопроса в день для различных дисциплин:
- SpringAtSO — два из лучших весенних вопросов от StackOverflow каждый день
- JavaTopSO — два из лучших вопросов о Java от StackOverflow каждый день
- AskUbuntuBest — два из лучших вопросов от AskUbuntu каждый день
- BestBash — два лучших вопроса Bash со всех сайтов StackExchange каждый день
- ServerFaultBest — два из лучших вопросов от ServerFault каждый день
Полная реализация этого клиента StackExchange находится на github .
Ссылка: Tweeting StackExchange с Spring Social — часть 1 от нашего партнера JCG Евгения Параскива в блоге baeldung .