Leçon ouverte "Création de clients REST au printemps"

Et encore une bonne journée! Très bientôt, nous commencerons la formation pour le prochain groupe, "Developer on the Spring Framework" , dans le cadre duquel nous avons mené une leçon ouverte, qui est déjà devenue une tradition en prévision du lancement. Lors de ce webinaire, nous avons parlé du développement de clients REST à l'aide de Spring, et avons également appris en détail des technologies telles que Spring Cache, Spring Retry et Hystrix.

Conférencier: Yuri Dvorzhetsky - formateur au Luxoft Training Center, développeur principal, candidat en sciences physiques et mathématiques.

Le webinaire a été suivi par un public complètement différent, évaluant leur connaissance du printemps dans un intervalle de 0 à 6 points sur une échelle de 10 points.Cependant, à en juger par les critiques, la leçon ouverte semblait utile même aux utilisateurs expérimentés.



Quelques mots sur le printemps 5

Comme vous le savez, le Spring Framework est un framework universel et assez populaire pour la plate-forme Java. Spring se compose d'une masse de sous-projets ou modules, ce qui vous permet de résoudre de nombreux problèmes. En fait, il s'agit d'une grande collection de «cadres dans un cadre», par exemple, seulement quelques-uns d'entre eux:

  • Spring IoC + AOP = Context,
  • Spring JDBC,
  • ORM de printemps,
  • Spring Data (il s'agit d'un ensemble de sous-projets),
  • Spring MVC, Spring WebFlux,
  • Sécurité printanière
  • Spring Cloud (il s'agit d'un ensemble encore plus large de sous-projets)
  • Lot de printemps,
  • Botte de printemps.


Spring remplace la programmation par certaines tâches de configuration, mais la configuration devient parfois juste un cauchemar. Pour créer rapidement des applications de production, ils utilisent Spring Boot . Il s'agit d'un cadre spécial qui contient un ensemble de démarreurs («starter»), qui simplifient la configuration des cadres Spring et d'autres technologies.

Pour montrer certaines des fonctionnalités de Spring, le thème du blocage des sites est parfait, car il est maintenant à la mode)). Si vous souhaitez participer activement à la leçon et à la pratique, il est recommandé de télécharger le référentiel avec le code serveur suggéré par l'enseignant. Nous utilisons la commande suivante:

git clone git@github.com:ydvorzhetskiy/sb-server.git

Ensuite, lancez, par exemple, comme ceci:

mvnw spring-boot:run

La plus grande réussite de Spring Boot est la possibilité de démarrer le serveur en exécutant simplement la classe Main dans IntelliJ IDEA.

Le fichier BlockedSite.java contient notre code source:

 package ru.otus.demoserver.domain; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class BlockedSite { @Id @GeneratedValue private int id; private String url; 


Et voici le contenu du contrôleur BlockedSitesController.java:

 package ru.otus.demoserver.rest; @RestController public class BlockedSitesController { private final Logger logger = LoggerFactory.getLogger(BlockedSitesController.class); private final BlockedSitesRepository repository; public BlockedSitesController(BlockedSitesRepository repository) { this.repository = repository; } @GetMapping("/blocked-sites") public List<BlockedSite> blockedSites() { logger.info("Request has been performed"); return repository.findAll(); } } 



Faites également attention à la base de données imbriquée dans pom.xml:

  <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>ru.otus</groupId> <artifactId>demo-server</artifactId> <version>0.0.1-SNAPSHOT</version> <url>demo-server</url> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 

Maintenant, d'une manière simple et sans prétention, nous enregistrons deux sites bloqués (DemoServerApplication.java) dans notre base de données via le référentiel:

 package ru.otus.demoserver; @SpringBootApplication public class DemoServerApplication { public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run(DemoServerApplication.class, args); BlockedSitesRepository repository = ctx.getBean(BlockedSitesRepository.class); repository.save(new BlockedSite("https://telegram.org/")); repository.save(new BlockedSite("https://azure.microsoft.com/")); } } 

Il reste à démarrer le serveur à l'aide de Spring Boot et à ouvrir l'URL appropriée sur l'hôte local (localhost:8080/blocked-sites) . Dans le même temps, notre serveur nous renverra une liste des sites que nous avons bloqués, c'est-à-dire les sites que nous avons ajoutés via la base de données.

Eh bien, il est temps d'écrire un client sur ce serveur. Mais avant de passer à cela, vous devez vous rappeler quelque chose.

Retraite théorique

Énumérons quelques méthodes HTTP (verbes):

  • GET - obtenir une entité ou une liste;
  • POST - création d'entité;
  • PUT - changer d'entité;
  • PATCH - changement d'entité (RFC -...);
  • DELETE - supprimer une entité;
  • HEAD, OPTIONS - méthodes «délicates» pour la prise en charge du protocole HTTP et des services REST en général;
  • TRACE est une méthode obsolète qui n'est pas utilisée.

On ne peut que rappeler une propriété aussi importante que l' idempotence . En termes simples, quel que soit le nombre de fois où vous appliquez une opération, son résultat sera le même que si vous ne l'avez appliqué qu'une seule fois. Par exemple, vous avez salué un homme le matin en disant "Bonjour!" En conséquence, votre ami entre dans un état "bonjour" :-). Et si vous lui dites «bonjour!» Plusieurs fois dans la journée, rien ne changera, il restera dans le même état.

Maintenant, réfléchissons à laquelle des méthodes HTTP ci-dessus sont idempotentes? Bien sûr, il est entendu que vous observez la sémantique. Si vous ne le savez pas, le professeur vous en dira plus à ce sujet, à partir de la 26e minute de la vidéo.

REPOS

Pour écrire un contrôleur REST, vous devez vous rappeler ce qu'est REST:

  • REST - REreprésentation State Transfer;
  • C'est un style architectural, pas une norme;
  • c'est, en fait, un ensemble de principes-restrictions;
  • REST était il y a longtemps, mais le terme est apparu relativement récemment;
  • L'application Web de style REST est appelée RESTful, son API dans ce cas est l'API RESTful (l'antonyme est Stateful);
  • REST s'appelle maintenant comme ils veulent ...

Premièrement, si nous parlons d'interaction sous la forme d'un client-serveur, alors elle doit être construite sous la forme d'une demande-réponse. Oui, l'interaction n'est pas toujours construite de cette façon, mais maintenant cette interaction est extrêmement courante, et pour les applications Web, quelque chose d'autre semble très étrange. Mais, par exemple, les sockets Web - ce n'est tout simplement pas REST.

Deuxièmement, la limitation la plus importante de REST est l’absence d’état client sur le serveur. Il est supposé que le client transmet toujours l'état nécessaire au serveur à chaque demande, c'est-à-dire que l'état est enregistré côté client et qu'il n'y a aucune session sur le serveur.

Comment écrire un client au printemps

Pour continuer, considérez et exécutez le client (utilisez le lien vers le référentiel):

 git clone git@github.com:ydvorzhetskiy/sb-client.git 

 mvnw spring-boot:run 

Il s'agit d'une application client et console déjà écrite, pas d'un serveur Web.

Nous regardons les dépendances:

 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>ru.otus</groupId> <artifactId>demo-client</artifactId> <version>0.0.1-SNAPSHOT</version> <url>demo-client</url> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!--   RestTemplate,    - --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.1.4.RELEASE</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency> <!-- Cache --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!-- Retry --> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency> <!-- Hystrix --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.0.2.RELEASE</version> </dependency> <dependency> <groupId>com.netflix.hystrix</groupId> <artifactId>hystrix-javanica</artifactId> <version>1.5.12</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 

Le client a une configuration:

1. RestTemplateConfig.java

 package ru.otus.democlient.config; @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { return restTemplateBuilder .setConnectTimeout(Duration.ofSeconds(2)) .setReadTimeout(Duration.ofSeconds(3)) .build(); } 

2. CacheConfig.java

 package ru.otus.democlient.config; @Configuration public class CacheConfig { @Bean public CacheManager cacheManager() { return new ConcurrentMapCacheManager("sites"); } } 

Et voici le contenu du fichier SiteServiceRest.java:

 package ru.otus.democlient.service; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.annotation.Cacheable; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.Collections; import java.util.List; @Service public class SiteServiceRest implements SiteService { private final RestTemplate restTemplate; private final String serverUrl; public SiteServiceRest( RestTemplate restTemplate, @Value("${application.server.url}") String serverUrl ) { this.restTemplate = restTemplate; this.serverUrl = serverUrl; } @Override public List<SiteInfo> findAllBlockedSites() { return restTemplate.exchange( serverUrl + "/blocked-sites", HttpMethod.GET, null, new ParameterizedTypeReference<List<SiteInfo>>() { } ).getBody(); } public List<SiteInfo> getDefaultSites() { return Collections.singletonList(new SiteInfo() {{ setUrl("http://vk.com/"); }}); } } 

Résumez légèrement:

  1. Les demandes sont effectuées via RestTemplate.
  2. RestTemplate peut être personnalisé, et c'est un bean régulier.
  3. Jackson est utilisé pour mapper JSON en objets.
  4. Ensuite - seulement votre vol de fantaisie (les détails sur le lancement du client sont dans la vidéo).

Chers collègues, le webinaire s'est avéré très informatif, par conséquent, afin de ne rien manquer, il est préférable de le regarder complètement. Vous allez essayer la véritable API «en combat», ajouter @Cacheable au service, travailler avec Spring Retry, en savoir plus sur Hystrix et bien plus encore. Nous vous invitons également à la journée portes ouvertes du printemps, qui se tiendra très prochainement.

Et, comme d'habitude, nous attendons vos commentaires sur la dernière leçon ouverte!

Source: https://habr.com/ru/post/fr440046/


All Articles