Open lesson "Creating REST-clients on Spring"

    And again, good day! Very soon we will start training the next group “Developer on the Spring Framework” , in connection with which we conducted an open lesson, which has become a tradition on the eve of the launch. This webinar talked about developing REST clients using Spring, and also learned in detail about technologies such as Spring Cache, Spring Retry, and Hystrix.

    Lecturer: Yuri Dvorzhetsky - a trainer at Luxoft Training Center, a leading developer, Candidate of Physical and Mathematical Sciences.

    The webinar was visited by a completely different audience, assessing their knowledge of Spring within 0-6 points on a 10-point scale, but judging by the reviews, the open lesson seemed useful even to experienced users.



    A few words about Spring 5

    As you know, the Spring Framework is a versatile and quite popular framework for the Java platform. Spring consists of a mass of subprojects or modules that allows you to solve many problems. In fact, this is a large collection of frameworks in the framework, for example, just a few of them:

    • Spring IoC + AOP = Context,
    • Spring jdbc
    • Spring ORM,
    • Spring Data (this is a whole set of subprojects),
    • Spring MVC, Spring WebFlux,
    • Spring Security,
    • Spring Cloud (it's an even bigger set of sub-projects),
    • Spring Batch,
    • Spring Boot.


    Spring replaces the programming of some tasks with configuration, however, configuration sometimes turns into just a nightmare. To quickly create production-grade applications, use Spring Boot . This is a special framework that contains a set of starters ('starter') that simplify the configuration of Spring frameworks and other technologies.

    To show some features of the work of Spring, the theme of blocking sites is perfect, as it is now fashionable)). If you want to actively participate in the lesson and practice, it is recommended to download the repository with the server code offered by the teacher. Use the following command:

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

    Next, just run, for example, like this:

    mvnw spring-boot:run

    The biggest achievement of Spring Boot is the ability to start the server by simply launching the Main class in IntelliJ IDEA.

    The file BlockedSite.java contains our source code:

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


    But the contents of the BlockedSitesController.java controller:

    package ru.otus.demoserver.rest;
    @RestController
    public classBlockedSitesController{
            	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();
            	}
    }



    Also note the nested database in pom.xml:

    <?xml version="1.0" encoding="UTF-8"?><projectxmlns="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>

    Now, in a simple and straightforward way, we save two blocked sites (DemoServerApplication.java) to our database via the repository:

    package ru.otus.demoserver;
    @SpringBootApplication
    public classDemoServerApplication{
            	public staticvoid 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/"));
            	}
    }

    It remains to start the server using Spring Boot and open the corresponding URL on the local host (localhost:8080/blocked-sites). At the same time, our server will return to us a list of sites blocked by us, that is, those sites that we added through the database.

    Well, it's time to write a client to this server. But before you go to this, you need to remember something.

    Theoretical retreat

    Let us list some HTTP methods (verbs):

    • GET - getting an entity or list;
    • POST - create entity;
    • PUT - change entity;
    • PATCH - change entity (RFC -...);
    • DELETE - delete an entity;
    • HEAD, OPTIONS - “tricky” methods to support the HTTP protocol and, in general, REST services;
    • TRACE is an obsolete method that is not used.

    It is impossible not to recall such an important property as idempotency . In simple terms, no matter how many times you use an operation, its result will be the same, as if you applied it only once. For example, you greeted the person in the morning, saying “Hello!” As a result, your friend goes into the “greeted” state :-). And if you say hello several times during the day, nothing will change, it will remain in the same state.

    And now, let's think about which of the above HTTP methods are idempotent? Of course, it is implied that you observe semantics. If you do not know, then the teacher tells more about this, starting with the 26th minute of the video.

    REST

    In order to write a REST controller, you need to remember what REST is:

    • REST — REpresentational State Transfer;
    • это архитектурный стиль, а не стандарт;
    • это, по сути, набор принципов-ограничений;
    • REST был давно, но термин появился сравнительно недавно;
    • Web-приложение в стиле REST называется RESTful, его API в таком случае — RESTful API (антоним — Stateful);
    • REST-ом сейчас называют всё что хотят…

    First, if we talk about interaction in the form of a client-server, then it needs to be built in the form of a request-response. Yes, the interaction is not always built that way, but now this interaction is extremely common, and for web applications, something else looks quite strange. And here, for example, web sockets is just not REST.

    Secondly, the most important restriction in REST is the lack of client status on the server. It is assumed that the client server always sends all the necessary state with each request, that is, the state is saved on the client side, and there are no sessions on the server.

    How to write a client on Spring

    To continue work, consider and run the client (use the link to the repository):

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

    mvnw spring-boot:run

    This is a client and console application already written, not a web server.

    See dependencies:

    <?xml version="1.0" encoding="UTF-8"?><projectxmlns="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>

    The client has the configuration:

    1. RestTemplateConfig.java

    package ru.otus.democlient.config;
    @Configuration
    public classRestTemplateConfig{
            	@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 classCacheConfig{
            	@Bean
            	public CacheManager cacheManager() {
    	    	returnnew ConcurrentMapCacheManager("sites");
            	}
    }

    But the contents of the SiteServiceRest.java file:

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

    Slightly summarize:

    1. Requests are made via RestTemplate.
    2. RestTemplate can be customized, and this is a regular bean.
    3. Jackson is used for mapping JSON into objects.
    4. Further - only your flight of fancy (details about the launch of the client are in the video).

    Colleagues, the webinar turned out to be very informative, therefore, in order not to miss anything, it’s better to watch it completely. You will try a real API "in combat conditions", add @Cacheableto the service, work with Spring Retry, learn about Hystrix and much more. We also invite you to the Spring Open Day , which will take place very soon.

    And, as usual, we are waiting for your comments on the past open lesson!

    Also popular now: