Client REST StackExchange avec Spring et RestTemplate

Cet article couvrira un projet parallèle rapide - un bot pour ** tweeter automatiquement les questions les plus fréquentes des différents Q

Le but initial de cette implémentation est de ne pas être un client à part entière pour l’ensemble de l’API StackExchange - cela échapperait à la portée de ce projet. La seule raison pour laquelle le client existe, c’est que je ne pourrais pas en installer un qui fonctionnerait avec la version 2.x de l’API officielle.

1. Les dépendances Maven

Pour utiliser l’API REST StackExchange, nous aurons besoin de très peu de dépendances - essentiellement un client HTTP - le Apache HttpClient suffira à cela:

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

Le lien:/comment-utiliser-resttemplate-with-basic-authentication-in-spring # resttemplate[Spring RestTemplate ]aurait également pu être utilisé pour interagir avec l’API HTTP, mais cela aurait introduit beaucoup d’autres fonctionnalités de Spring dépendances liées au projet, dépendances qui ne sont pas strictement nécessaires, donc HttpClient gardera les choses simples et légères.

2. Les questions client

Ce client a pour objectif de consommer le service REST /questions que StackExchange publishes , et non de fournir un client à usage général pour l’ensemble des API StackExchange - le but de cet article, nous allons seulement regarder cela. La communication HTTP actuelle avec HTTPClient est relativement simple:

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

Cette interaction simple convient parfaitement pour obtenir les questions JSON brutes publiées par l’API. La prochaine étape consistera à traiter ce fichier JSON. Il y a un détail pertinent ici - et c’est l’argument questionsUri de la méthode - plusieurs API StackExchange peuvent publier des questions (comme le suggère la official documentation , et cette méthode doit être suffisamment souple pour les consommer toutes. Elle peut consommer, par exemple, l’API la plus simple qui renvoie des questions en définissant questionUri sur https://api.stackexchange.com/2.1/questions? Site = stackoverflow ou en consommant la balise https://api.stackexchange.com/2.1/tags/\ {tags}/faq? Site = stackoverflow API à la place, selon les besoins du client.

Une requête adressée à l’API StackExchange est entièrement configurée avec des paramètres de requête, même pour les requêtes de recherche avancées les plus complexes: aucun corps n’est envoyé. Pour construire la questionsUri , nous allons construire une classe RequestBuilder fluide qui utilisera couramment URIBuilder ** de la bibliothèque HttpClient. Ceci s’occupera d’encoder correctement l’URI et de s’assurer en général que le résultat final est valide:

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

Alors maintenant, pour construire un URI valide pour l’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. Test du client

Le client générera une sortie JSON brute, mais pour tester cela, nous aurons besoin d’une bibliothèque de traitement JSON, plus précisément Jackson 2 :

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

Les tests que nous examinerons interagiront avec l’API StackExchange actuelle:

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

Le premier test a vérifié que la réponse fournie par l’API était bien 200 OK. La requête GET pour extraire les questions a donc abouti. Une fois cette condition de base assurée, nous passons à la représentation - spécifiée par l’en-tête HTTP Content-Type - qui doit être JSON. Ensuite, nous analysons le code JSON et nous vérifions que cette sortie contient des questions: la logique d’analyse elle-même est simple et de bas niveau, ce qui suffit aux fins du test.

Notez que ces demandes comptent dans vos rate limits spécifiées par l’API - pour cette raison, les tests en direct sont exclus de la version standard de 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. L’étape suivante

Le client actuel se concentre uniquement sur un seul type de ressource parmi les nombreux types disponibles publiés par les API StackExchange. En effet, son objectif initial est limité: il suffit de permettre à un utilisateur de consommer Questions à partir des différents sites du portefeuille StackExchange. Par conséquent, le client peut être amélioré au-delà de la portée de ce cas d’utilisation initial afin de pouvoir utiliser les types d’ensemble de l’API . L’implémentation est également beaucoup raw - après avoir consommé le service Questions REST, elle renvoie simplement la sortie JSON sous forme de chaîne - et non n’importe quel type de modèle Questions à partir de cette sortie. Ainsi, une prochaine étape potentielle serait de décomposer ce JSON en un DTO de domaine approprié et de le renvoyer au lieu d’un JSON brut.

5. Conclusion

Le but de cet article était de montrer comment commencer à construire une intégration avec l’API StackExchange ou une API basée sur HTTP. Il expliquait comment écrire des tests d’intégration avec l’API dynamique et s’assurer que l’interaction de bout en bout fonctionne réellement.

La deuxième partie de cet article montrera comment interagir avec l’API Twitter en utilisant la bibliothèque Spring Social et comment utiliser le client StackExchange que nous avons construit ici pour répondre aux questions sur un nouveau compte Twitter.

J’ai déjà mis en place quelques comptes Twitter qui twittent à présent les 2 questions les plus posées par jour, dans différentes disciplines:

questions de StackOverflow chaque jour ** JavaTopSO - Deux des meilleurs Java

questions de StackOverflow chaque jour ** AskUbuntuBest - Deux des meilleurs

questions de AskUbuntu chaque jour ** BestBash - Deux des meilleurs Bash

questions de tous les sites StackExchange chaque jour ** ServerFaultBest - Deux des meilleurs

questions de ServerFault chaque jour

L’implémentation complète de ce client StackExchange est on github .