الدرس المفتوح "إنشاء عملاء REST في الربيع"

ومرة أخرى ، يوم جيد! قريباً جداً ، سنبدأ التدريب للمجموعة التالية ، "Developer on the Spring Framework" ، التي أجرى فيها درسًا مفتوحًا ، والذي أصبح بالفعل تقليدًا متوقعًا للإطلاق. تحدث ندوة الويب هذه حول تطوير عملاء REST باستخدام Spring ، وتعلموا أيضًا بالتفصيل حول تقنيات مثل Spring Cache و Spring Retry و Hystrix.

المحاضر: يوري دفورشتسكي - مدرب في مركز لوكسوف للتدريب ، مطور رئيسي ، مرشح للعلوم الفيزيائية والرياضية.

حضر الندوة عبر الويب جمهور مختلف تمامًا ، حيث قاموا بتقييم معرفتهم بـ Spring خلال 0-6 نقاط على مقياس من 10 نقاط ، ومع ذلك ، واستنادا إلى المراجعات ، بدا الدرس المفتوح مفيدًا حتى للمستخدمين ذوي الخبرة.



بضع كلمات عن الربيع 5

كما تعلمون ، فإن Spring Framework هو إطار عالمي وشعبي إلى حد ما لمنصة Java. يتكون الربيع من مجموعة من المشاريع الفرعية أو الوحدات النمطية ، والتي تتيح لك حل العديد من المشكلات. في الواقع ، هذه مجموعة كبيرة من "الأطر في إطار عمل" ، على سبيل المثال ، عدد قليل منها:

  • ربيع IoC + AOP = السياق ،
  • الربيع JDBC ،
  • ربيع ORM ،
  • بيانات الربيع (هذه مجموعة كاملة من المشاريع الفرعية) ،
  • Spring MVC، Spring WebFlux،
  • أمن الربيع
  • Spring Cloud (هذه مجموعة أكبر من المشاريع الفرعية)
  • دفعة الربيع ،
  • التمهيد الربيع.


يستبدل Spring البرمجة ببعض المهام من أجل التكوين ، لكن التكوين يتحول أحيانًا إلى كابوس. لإنشاء تطبيقات على مستوى الإنتاج بسرعة ، يستخدمون Spring Boot . هذا هو إطار خاص يحتوي على مجموعة من المبتدئين ("بداية") ، والتي تبسط تكوين أطر الربيع وغيرها من التقنيات.

لإظهار بعض ميزات Spring ، يكون موضوع حظر المواقع مثاليًا ، حيث أصبح من المألوف الآن)). إذا كنت ترغب في المشاركة بنشاط في الدرس والتمرين ، يوصى بتنزيل المستودع برمز الخادم الذي اقترحه المعلم. نستخدم الأمر التالي:

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

بعد ذلك ، قم فقط بتشغيل ، على سبيل المثال ، مثل هذا:

mvnw spring-boot:run

أكبر إنجاز لـ Spring Boot هو القدرة على بدء تشغيل الخادم عن طريق تشغيل الفئة الرئيسية في IntelliJ IDEA.

يحتوي ملف BlockedSite.java على الكود المصدري الخاص بنا:

 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; 


وهنا محتويات وحدة تحكم 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(); } } 



انتبه أيضًا إلى قاعدة البيانات المتداخلة في 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> 

الآن ، بطريقة بسيطة وبسيطة ، نقوم بحفظ موقعين محظورين (DemoServerApplication.java) في قاعدة البيانات الخاصة بنا من خلال المستودع:

 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/")); } } 

يبقى أن تبدأ الخادم باستخدام Spring Boot وفتح عنوان URL المناسب على المضيف المحلي (localhost:8080/blocked-sites) المضيف المحلي (localhost:8080/blocked-sites) . في الوقت نفسه ، سيعود الخادم الخاص بنا إلينا قائمة بالمواقع التي حظرناها ، أي تلك المواقع التي أضفناها عبر قاعدة البيانات.

حسنًا ، لقد حان الوقت لكتابة عميل إلى هذا الخادم. ولكن قبل الانتقال إلى هذا ، تحتاج إلى تذكر شيء ما.

تراجع نظري

دعونا سرد بعض أساليب HTTP (الأفعال):

  • الحصول على - الحصول على كيان أو قائمة.
  • وظيفة - إنشاء كيان.
  • وضع - تغيير الكيان ؛
  • PATCH - تغيير الكيان (RFC -...) ؛
  • حذف - حذف الكيان.
  • HEAD ، OPTIONS - طرق "صعبة" لدعم بروتوكول HTTP وخدمات REST بشكل عام ؛
  • TRACE هي طريقة قديمة لا يتم استخدامها.

لا يسع المرء إلا أن يتذكر خاصية مهمة مثل الشغف . بعبارات بسيطة ، بغض النظر عن عدد المرات التي تطبق فيها العملية ، ستكون النتيجة هي نفسها كما لو كنت طبقتها مرة واحدة فقط. على سبيل المثال ، استقبلت رجلاً في الصباح ، قائلة "مرحبًا!" نتيجة لذلك ، يذهب صديقك إلى حالة "الترحيب" :-). وإذا قلت "مرحبًا!" له عدة مرات خلال اليوم ، فلن يتغير شيء ، وسيبقى في نفس الحالة.

الآن ، دعنا نفكر في أي من أساليب HTTP المذكورة أعلاه غير مناسبة؟ بالطبع ، من المفهوم أنك تلاحظ الدلالات. إذا كنت لا تعرف ، فسيعلمك المعلم أكثر حول هذا الموضوع ، بدءًا من الدقيقة 26 من الفيديو.

الراحة

من أجل كتابة وحدة تحكم REST ، يجب أن تتذكر ماهية REST:

  • REST - نقل الحالة التمثيلية ؛
  • إنه أسلوب معماري ، وليس معيارًا ؛
  • إنه ، في الواقع ، مجموعة من قيود المبادئ ؛
  • كان REST منذ زمن طويل ، ولكن ظهر المصطلح مؤخرًا نسبيًا ؛
  • يسمى تطبيق الويب REST-style RESTful ، API الخاص به في هذه الحالة هو RESTful API (المتناقض هو Stateful) ؛
  • يسمى الآن REST كل ما يريدون ...

أولاً ، إذا تحدثنا عن التفاعل في شكل خادم عميل ، فيجب أن يتم بناؤه في شكل طلب استجابة. نعم ، لا يتم بناء التفاعل دائمًا بهذه الطريقة ، ولكن هذا التفاعل شائع للغاية ، وبالنسبة لتطبيقات الويب ، يبدو شيء آخر غريبًا للغاية. ولكن ، على سبيل المثال ، مآخذ الويب - وهذا ليس مجرد REST.

ثانياً ، أهم قيود REST هو عدم وجود حالة العميل على الخادم. من المفترض أن يقوم العميل دائمًا بتمرير الحالة اللازمة إلى الخادم مع كل طلب ، أي يتم حفظ الحالة من جانب العميل ، ولا توجد جلسات على الخادم.

كيفية كتابة عميل في الربيع

للمتابعة ، ضع في الاعتبار العميل وقم بتشغيله (استخدم الرابط إلى المستودع):

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

 mvnw spring-boot:run 

هذا تطبيق عميل ووحدة تحكم مكتوبة بالفعل ، وليس خادم ويب.

نحن ننظر إلى التبعيات:

 <?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> 

العميل لديه التكوين:

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

وهنا محتويات ملف 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/"); }}); } } 

تلخيص قليلا:

  1. يتم تقديم الطلبات من خلال RestTemplate.
  2. يمكن تخصيص RestTemplate ، وهذا هو الحبة العادية.
  3. يستخدم جاكسون لتعيين JSON في الكائنات.
  4. بعد ذلك - فقط رحلة الهوى الخاصة بك (توجد تفاصيل حول إطلاق العميل في الفيديو).

أيها الزملاء ، تحولت الندوة عبر الإنترنت إلى أنها كانت مفيدة للغاية ، لذلك ، حتى لا تفوت أي شيء ، فمن الأفضل مشاهدته بالكامل. @Cacheable واجهة برمجة التطبيقات الحقيقية "في القتال" ، أضف @Cacheable إلى الخدمة ، والعمل مع Spring Retry ، @Cacheable على @Cacheable وأكثر من ذلك بكثير. ندعوك أيضًا إلى يوم الربيع المفتوح الذي سيعقد قريبًا.

وكالعادة ، نحن في انتظار تعليقاتكم على الدرس المفتوح الماضي!

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


All Articles