Това е последната част от нашия преглед на REST. В предишните части разгледахме:
Създаване на проект
В този раздел ще създадем малко RESTful приложение с помощта на Spring Boot. Нашето приложение ще реализира CRUD (Създаване, Четене, Актуализиране, Изтриване) операции на клиентите от примера в предишната част на прегледа. За да започнем, ще създадем ново приложение Spring Boot чрез менюто: File -> New -> Project... В прозореца, който се отваря, изберете Spring Initializr и посочете Project SDK: Щракнете върху бутона "Next". В следващия прозорец посочете „Проект Maven“ като тип на проекта, посочете „Група“ и „Артефакт“: Щракнете върху бутона „Напред“. В следващия прозорец трябва да изберем компонентите на Spring Framework, необходими за проекта. Spring Web ще ни е достатъчен: Кликнете върху бутона "Напред". Сега остава само да посочите името на проекта и местоположението му във файловата система: Щракнете върху бутона "Край". Проектът е създаден и сега можем да видим неговата структура: IDEA генерира дескриптор за внедряване на Maven (pom.xml) и основния клас на приложението (RestExampleApplication
) за нас. Ето How изглеждат:
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 https://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.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.codegym.lessons/groupId>
<artifactId>rest_example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rest_example</name>
<description>REST example project</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-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
RestExampleApplication:
@SpringBootApplication
public class RestExampleApplication {
public static void main(String[] args) {
SpringApplication.run(RestExampleApplication.class, args);
}
}
Създаване на REST функционалност
Нашето приложение е система за управление на клиенти. И така, първото нещо, което трябва да направим, е да създадем обект клиент. Това ще бъде POJO (обикновен стар Java обект) клас. Създайтеmodel
пакет вътре в com.codegym.lessons.rest_example
пакета. Вътре в model
пакета създайте Customer
:
public class Customer {
private Integer id;
private String name;
private String email;
private String phone;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
Услугата ще прилага CRUD операции на клиенти. Следващата стъпка е да създадете услуга, която ще изпълнява тези операции. В com.codegym.lessons.rest_example
пакета създайте service
пакет. И вътре в това създайте CustomerService
интерфейс. Ето codeа на интерфейса с коментари:
public interface CustomerService {
/**
* Creates a new customer
* @param customer - Customer to be created
*/
void create(Customer customer);
/**
* Returns a list of all existing customers
* @return List of customers
*/
List<Customer> readAll();
/**
* Returns a customer based on its ID
* @param id - Customer ID
* @return - Customer object with the given ID
*/
Customer read(int id);
/**
* Updates the customer with the given ID,
* according to the passed customer
* @param customer - Customer to use to update the data
* @param id - ID of the customer you want to update
* @return - true if the data has been updated, otherwise false
*/
boolean update(Customer customer, int id);
/**
* Deletes the customer with the given ID
* @param id - ID of the customer to be deleted
* @return - true if the customer was deleted, otherwise false
*/
boolean delete(int id);
}
След това трябва да внедрим този интерфейс. Сега a Map<Integer, Customer>
ще съхранява нашите клиенти. Ключовете на картата ще бъдат идентификаторите на клиентите, а стойностите ще бъдат самите клиенти. Това се прави, за да не се претоварва този пример със спецификата на работа с реална база данни. В бъдеще обаче ще можем да напишем друга реализация на интерфейса, която ще направи възможно свързването с реална база данни. В service
пакета създайте имплементация на CustomerService
интерфейса:
@Service
public class CustomerServiceImpl implements CustomerService {
// Customer repository
private static final Map<Integer, Customer> CUSTOMER_REPOSITORY_MAP = new HashMap<>();
// Variable for generating a customer ID
private static final AtomicInteger CUSTOMER_ID_HOLDER = new AtomicInteger();
@Override
public void create(Customer customer) {
final int customerId = CUSTOMER_ID_HOLDER.incrementAndGet();
customer.setId(customerId);
CUSTOMER_REPOSITORY_MAP.put(customerId, customer);
}
@Override
public List<Customer> readAll() {
return new ArrayList<>(CUSTOMER_REPOSITORY_MAP.values());
}
@Override
public Customer read(int id) {
return CUSTOMER_REPOSITORY_MAP.get(id);
}
@Override
public boolean update(Customer customer, int id) {
if (CUSTOMER_REPOSITORY_MAP.containsKey(id)) {
customer.setId(id);
CUSTOMER_REPOSITORY_MAP.put(id, customer);
return true;
}
return false;
}
@Override
public boolean delete(int id) {
return CUSTOMER_REPOSITORY_MAP.remove(id) != null;
}
}
Анотацията @Service
казва на пролетта, че този клас е услуга. Това е специален тип клас, който реализира няHowва логика на бизнес приложение. Впоследствие, благодарение на тази анотация, Spring ще използва инжектиране на зависимости, за да ни предостави екземпляр на този клас на всички места, където е необходимо. Сега е време да създадете контролер. Това е специален клас, в който ще внедрим логиката за обработка на клиентски заявки, изпратени до крайни точки (URI). За да направим всичко това по-ясно, ние ще създадем този клас постепенно. Първо създайте самия клас и добавете зависимост от CustomerService
:
@RestController
public class CustomerController {
private final CustomerService customerService;
@Autowired
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
}
Нека обясним анотациите: @RestController казва на Spring, че този клас е REST контролер. С други думи, този клас реализира логиката за обработка на клиентски заявки. @Autowired казва на Spring, че тук трябва да се добави зависимост. Предаваме CustomerService
интерфейса на конструктора. По-рано маркирахме изпълнението на тази услуга с анотацията @Service
и сега Spring ще може да прехвърли екземпляр на това изпълнение към конструктора на контролера. След това ще приложим всеки метод на контролер за обработка на CRUD операции. Нека започнем с операцията за създаване. За да направим това, ние пишем create
метод:
@PostMapping(value = "/customers")
public ResponseEntity<?> create(@RequestBody Customer customer) {
customerService.create(customer);
return new ResponseEntity<>(HttpStatus.CREATED);
}
Нека анализираме този метод: @PostMapping(value = "/customers")
означава, че този метод обработва POST заявки, изпратени до address "/customers". Методът връща ResponseEntity<?>
. A ResponseEntity
е специален клас за връщане на отговори. По-късно ще го използваме, за да върнем HTTP code на състоянието на клиента. Методът има @RequestBody Customer customer
параметър. Стойността на този параметър идва от тялото на заявката. Анотацията @RequestBody
показва това. Вътре в тялото на метода извикваме метода create()
на предварително създадената услуга и го предаваме на клиентския контролер, получен в параметрите. След това връщаме състоянието „201 Created“, като създаваме нов ResponseEntity
обект и предаваме съответното HttpStatus
поле enum към него. След това ще приложимread
операция: Първо ще приложим операцията, за да получим списък с всички налични клиенти:
@GetMapping(value = "/customers")
public ResponseEntity<List<Customer>> read() {
final List<Customer> customers = customerService.readAll();
return customers != null && !customers.isEmpty()
? new ResponseEntity<>(customers, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Нека се потопим в: @GetMapping(value = "/customers")
— всичко тук е подобно на @PostMapping
анотацията, но сега обработваме GET заявки. Този път връщаме ResponseEntity<List<Customer>>
, и в допълнение към HTTP статус, ще върнем и тяло на отговора, което ще бъде списъкът с клиенти. В REST контролерите на Spring всичко е POJO обекти и колекции от POJO обекти, които се връщат като тела на отговор и автоматично се сериализират в JSON, освен ако не е указано друго. Това ни устройва напълно. В рамките на метода използваме нашата услуга, за да получим списък с всички клиенти. След това, ако списъкът не е нула и не е празен, тогава използвамеResponseEntity
клас, за да върне списъка с клиенти и HTTP codeа за състояние "200 OK". В противен случай просто връщаме HTTP статус codeа „404 не е намерен“. Сега ще приложим възможността да получите клиент, използвайки неговия ID:
@GetMapping(value = "/customers/{id}")
public ResponseEntity<Customer> read(@PathVariable(name = "id") int id) {
final Customer customer = customerService.read(id);
return customer != null
? new ResponseEntity<>(customer, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Едно ново нещо тук е променливата път. Променливата е дефинирана в URI: value = "/customers/{id}"
. Посочваме го във фигурни скоби. И го получаваме като int
параметър на метода, използвайки @PathVariable(name = "id")
анотацията. Този метод ще приема заявки, изпратени до URI във формуляра /customers/{id}
, където {id}
представлява произволна числова стойност. Тази стойност впоследствие се предава чрез int id
променливата към параметъра на метода. В тялото получаваме обекта, Customer
използвайки нашата услуга и полученото id
. И след това, по аналогия със списъка, връщаме or състоянието „200 OK“ и Customer
самия обект, or просто състоянието „404 Не е намерен“, ако системата няма клиент с това id
. Все още трябва да приложим две операции: актуализиране и изтриване. Ето codeа за тези методи:
@PutMapping(value = "/customers/{id}")
public ResponseEntity<?> update(@PathVariable(name = "id") int id, @RequestBody Customer customer) {
final boolean updated = customerService.update(customer, id);
return updated
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
@DeleteMapping(value = "/customers/{id}")
public ResponseEntity<?> delete(@PathVariable(name = "id") int id) {
final boolean deleted = customerService.delete(id);
return deleted
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
Няма нищо съществено ново в тези методи, така че ще пропуснем подробното описание. Единственото нещо, което си струва да се спомене е, че update()
методът обработва PUT заявки ( @PutMapping
анотация), а delete()
методът обработва DELETE заявки ( DeleteMapping
анотация). Ето пълния code за контролера:
@RestController
public class CustomerController {
private final CustomerService customerService;
@Autowired
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
@PostMapping(value = "/customers")
public ResponseEntity<?> create(@RequestBody Customer customer) {
customerService.create(customer);
return new ResponseEntity<>(HttpStatus.CREATED);
}
@GetMapping(value = "/customers")
public ResponseEntity<List<Customer>> read() {
final List<Customer> customers = customerService.readAll();
return customers != null && !customers.isEmpty()
? new ResponseEntity<>(customers, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
@GetMapping(value = "/customers/{id}")
public ResponseEntity<Customer> read(@PathVariable(name = "id") int id) {
final Customer customer = customerService.read(id);
return customer != null
? new ResponseEntity<>(customer, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
@PutMapping(value = "/customers/{id}")
public ResponseEntity<?> update(@PathVariable(name = "id") int id, @RequestBody Customer customer) {
final boolean updated = customerService.update(customer, id);
return updated
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
@DeleteMapping(value = "/customers/{id}")
public ResponseEntity<?> delete(@PathVariable(name = "id") int id) {
final boolean deleted = customerService.delete(id);
return deleted
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
}
В резултат на това структурата на нашия проект е следната:
Пускане и тестване
За да стартирате нашето приложение, просто стартирайтеmain()
метода в RestExampleApplication
класа. Но за да тестваме RESTful уеб услуги, трябва да изтеглим допълнителен софтуер. Факт е, че GET заявките са доста лесни за изпращане от обикновен браузър, но обикновен браузър не може да изпраща POST, PUT и DELETE заявки. Не се притеснявайте: можете да използвате програма, наречена Postman, за да изпращате всяHowви HTTP заявки. Можете да го изтеглите от тук . След като изтеглим и инсталираме Postman, започваме да тестваме нашето приложение. За да направите това, отворете програмата и създайте нова заявка: Щракнете върху бутона "Ново" в горния ляв ъгъл. След това изберете „Заявка“: След това му дайте име и го запазете. Сега нека се опитаме да изпратим POST заявка до сървъра и да създадем първия клиент: По този начин създаваме няколко клиента. След това променяме типа заявка на GET и изпращаме заявката до сървъра:
Резюме
Честито! Покрихме достатъчно REST. Имаше голям обем материал, но се надяваме, че е бил полезен за вас:-
Научихме Howво е REST.
-
Научихме How се появи REST.
-
Говорихме за ограниченията и принципите зад този архитектурен стил:
- клиент-сървър архитектура
- без гражданство
- кеширане
- единен интерфейс
- слоеве
- code при поискване (по избор)
-
Проучихме предимствата, предоставени от REST
-
Разгледахме подробно How сървърът и клиентът си взаимодействат помежду си чрез HTTP протокола.
-
Разгледахме по-отблизо исканията и отговорите. Направихме дисекция на техните съставни части.
-
И накрая, получихме практически опит, като написахме собствено малко RESTful приложение, използвайки Spring Boot. И дори се научихме How да го тестваме с помощта на Postman.
Домашна работа
Опитайте следното:- Следвайки описанието по-горе, създайте свой собствен проект Spring Boot и приложете същата логика като в урока. Повторете всичко точно.
- Стартирайте приложението.
- Изтеглете и конфигурирайте Postman (or всеки друг инструмент за изпращане на заявки, например curl).
- Тествайте заявките POST и GET по същия начин, описан в урока.
- Тествайте сами заявките PUT и DELETE.
GO TO FULL VERSION