Эта статья будет посвящена быстрому побочному проекту - роботу, автоматически ** отправляющему в Твиттере топ-вопросы из различных вопросов.
Первоначальная цель этой реализации не состоит в том, чтобы быть полноценным клиентом для всего API StackExchange, что выходит за рамки данного проекта. Единственная причина, по которой существует Клиент, заключается в том, что я не смог найти такую, которая бы работала против версии 2.x официального API.
1. Зависимости Maven
Чтобы использовать REST API StackExchange, нам понадобится очень мало зависимостей - по сути, просто HTTP-клиент - Apache HttpClient прекрасно подойдет для этой цели:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.3</version>
</dependency>
Ссылка:/how-to-use-resttemplate-with-basic-authentication-in-spring # resttemplate[Spring RestTemplate ]также могла бы использоваться для взаимодействия с HTTP API, но это привело бы к появлению множества других Spring связанные зависимости в проекте, зависимости, которые не являются строго необходимыми, поэтому HttpClient сделает все легко и просто.
2. Клиент вопросов
Целью этого клиента является использование службы REST /questions , которую StackExchange publishes , а не предоставление универсального клиента для всех API-интерфейсов StackExchange - так для Цель этой статьи мы только рассмотрим. Фактическая HTTP-связь с использованием HTTPClient относительно проста:
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, которые могут публиковать вопросы (как предлагает official документация ) и этот метод должен быть достаточно гибким, чтобы использовать их все. Например, он может использовать самый простой 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 и, как правило, о том, что конечный результат верен:
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:
String params = new RequestBuilder().
add("order", "desc").add("sort", "votes").add("min", min).add("site", site).build();
return "https://api.stackexchange.com/2.1/questions" + params;
3. Тестирование клиента
Клиент выведет необработанный JSON, но для проверки нам потребуется библиотека обработки JSON, в частности Jackson 2 :
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.3</version>
<scope>test</scope>
</dependency>
Тесты, которые мы рассмотрим, будут взаимодействовать с реальным API StackExchange:
@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 и проверяем, что в этом выводе действительно есть Вопросы - сама логика синтаксического анализа является низкоуровневой и простой, что достаточно для целей теста.
Обратите внимание, что эти запросы учитываются для ваших ограничений rate , указанных API - по этой причине тесты Live исключаются из стандартной сборки Maven:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.17</version>
<configuration>
<excludes>
<exclude>** ** /** LiveTest.java</exclude>
</excludes>
</configuration>
</plugin>
4. Следующий шаг
Текущий Клиент ориентирован только на один тип Ресурса из множества доступных типов, опубликованных API-интерфейсами StackExchange. Это связано с тем, что его первоначальная цель ограничена - ему нужно только разрешить пользователю использовать Questions с различных сайтов в портфеле StackExchange. Следовательно, клиент может быть улучшен за пределами этого первоначального варианта использования, чтобы иметь возможность использовать otherother типы API . Реализация также очень сырая - после использования REST-службы вопросов она просто возвращает вывод JSON в виде строки, а не какую-либо модель вопросов из этого вывода. Таким образом, потенциальным следующим шагом будет демонтаж этого JSON в правильный домен DTO и возврат его обратно вместо необработанного JSON.
5. Заключение
Цель этой статьи состояла в том, чтобы показать, как начать создавать интеграцию с API StackExchange или действительно с API на основе HTTP. В нем рассказывалось о том, как писать интеграционные тесты для реального API и убедиться, что сквозное взаимодействие действительно работает.
Во второй части этой статьи будет показано, как взаимодействовать с API-интерфейсом Twitter с помощью библиотеки Spring Social, и как использовать клиент StackExchange, который мы создали здесь, чтобы твитить вопросы в новой учетной записи Twitter.
Я уже настроил несколько учетных записей в Твиттере, которые теперь пишут в Твиттере 2 главных вопроса в день для различных дисциплин:
-
SpringTip - два лучших источника весны
вопросы от StackOverflow каждый день ** JavaTopSO - два из лучших Java
вопросы от StackOverflow каждый день ** AskUbuntuBest - два лучших
вопросы от AskUbuntu каждый день ** BestBash - два лучших Bash
вопросы со всех сайтов StackExchange каждый день ** ServerFaultBest - два из лучших
вопросы от ServerFault каждый день
Полная реализация этого клиента StackExchange: on github .