この記事では、簡単なサイドプロジェクト、つまり自動的に実行するボットについて説明します。 さまざまなQ&AStackExchangeサイトからの上位の質問をツイートする 、 そのようなスタックオーバーフロー ServerFault スーパーユーザー 、など。 StackExchange API のシンプルなクライアントを構築し、 SpringSocialを使用してTwitterAPIとのやり取りを設定します。この最初の部分ではStackExchangeクライアントのみに焦点を当てます。 。

この実装の最初の目的は、 StackExchange API全体の本格的なクライアントではないことです。これは、このプロジェクトの範囲外です。 クライアントが存在する唯一の理由は、公式APIの2.xバージョンに対して機能するクライアントを微調整できなかったためです。

1. Mavenの依存関係

StackExchange REST APIを使用するには、依存関係はほとんど必要ありません。基本的にはHTTPクライアントだけです。ApacheHttpClientはこの目的に適しています。

<dependency>
   <groupId>org.apache.httpcomponents</groupId>
   <artifactId>httpclient</artifactId>
   <version>4.3.3</version>
</dependency>

Spring RestTemplateを使用してHTTPAPIとやり取りすることもできますが、これにより、プロジェクトに他の多くのSpring関連の依存関係が導入されます。依存関係は厳密には必要ないため、HttpClientは処理を維持します。軽くてシンプル。

2. 質問クライアント

このクライアントの目的は、StackExchangeが公開する/ questions RESTサービスを利用することであり、StackExchange API全体に汎用クライアントを提供することではありません。したがって、この記事の目的のために、それだけを見ます。 HTTPClientを使用した実際のHTTP通信は比較的簡単です。

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();
      }
   }
}

この単純な相互作用は、APIが公開する生のJSONに関する質問を取得するのに完全に適しています。次のステップは、そのJSONを処理することです。 ここに関連する詳細が1つあります-それはquestionsUriメソッド引数です-質問を公開できる複数のStackExchangeAPIがあり(公式ドキュメントが示唆するように)、このメソッドはそれらすべてを消費するのに十分な柔軟性。 たとえば、questionUrihttps://api.stackexchange.com/2.1/questions?site=stackoverflowに設定して質問を返す最も単純なAPIを使用するか、代わりに、クライアントのニーズに応じて、タグベースの https://api.stackexchange.com/2.1/tags/{tags}/faq?site=stackoverflowAPI。

StackExchange APIへのリクエストは、より複雑な高度な検索クエリの場合でも、クエリパラメーターを使用して完全に構成されます。送信される本文はありません。 questionUri を構築するために、HttpClientライブラリのURIBuilder利用する基本的な流暢なRequestBuilderクラスを構築します。 これにより、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();
   }
}

そこで、StackExchange APIの有効なURIを作成するには、次のようにします。

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処理ライブラリ、具体的には Jackson2が必要です。

<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.3.3</version>
   <scope>test</scope>
</dependency>

ここで説明するテストは、実際のStackExchangeAPIと相互作用します。

@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リクエストは実際に成功しました。 その基本的な条件が確認された後、 Content-Type HTTPヘッダーで指定されているように、JSONである必要がある表現に移りました。 次に、実際にJSONを解析し、その出力に実際に質問があることを確認します。解析ロジック自体は低レベルで単純であり、テストの目的には十分です。

これらのリクエストは、APIで指定されたレート制限にカウントされることに注意してください。そのため、ライブテストは標準の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. 次のステップ

現在のクライアントは、StackExchangeAPIによって公開されている多くの利用可能なタイプの単一タイプのリソースにのみ焦点を当てています。 これは、当初の目的が限られているためです。ユーザーがStackExchangeポートフォリオのさまざまなサイトからQuestionsを利用できるようにするだけで済みます。 その結果、クライアントは、この最初のユースケースの範囲を超えて改善され、他のタイプのAPIを使用できるようになります。 実装も非常にrawであり、Questions RESTサービスを使用した後、JSON出力を文字列として返すだけです。その出力からのQuestionsモデルはありません。 したがって、潜在的な次のステップは、このJSONを適切なドメインDTOにマーシャリング解除し、生のJSONの代わりにそれを返すことです。

5. 結論

この記事の目的は、StackExchange API、または実際にはHTTPベースのAPIとの統合の構築を開始する方法を示すことでした。 ライブAPIに対して統合テストを作成し、エンドツーエンドの対話が実際に機能することを確認する方法について説明しました。

この記事の第2部では、SpringSocialライブラリを使用してTwitterAPIを操作する方法と、ここで構築したStackExchangeクライアントを使用して新しいTwitterアカウントで質問をツイートする方法を示します。

私はすでにいくつかのTwitterアカウントを設定しており、さまざまな分野で1日あたり2つの上位の質問をツイートしています。

  • SpringTip –毎日StackOverflowからの春の最高の質問の2つ
  • JavaTopSO –毎日StackOverflowからの2つの最高のJava質問
  • AskUbuntuBest –毎日AskUbuntuからの2つの最高の質問
  • BestBash –毎日すべてのStackExchangeサイトからの2つの最高のBash質問
  • ServerFaultBest –毎日ServerFaultからの2つの最高の質問

このStackExchangeクライアントの完全な実装は、github上のです。