Dette er den sidste del af vores oversigt over REST. I de foregående dele dækkede vi:
Klik på knappen "Næste". I det næste vindue skal du angive "Maven Project" som projekttype, angive "Gruppe" og "Artefakt":
Klik på knappen "Næste". I det næste vindue skal vi vælge de Spring Framework-komponenter, der er nødvendige for projektet. Spring Web vil være tilstrækkeligt for os:
Klik på knappen "Næste". Nu er der kun tilbage at angive navnet på projektet og dets placering i filsystemet:
Klik på knappen "Udfør". Projektet er oprettet, og nu kan vi se dets struktur:
IDEA genererede en Maven deployment descriptor (pom.xml) og applikationens hovedklasse (
Klik på knappen "Ny" i øverste venstre hjørne. Vælg derefter "Request":
Giv det derefter et navn og gem det. Lad os nu prøve at sende en POST-anmodning til serveren og oprette den første kunde:
Vi skaber flere kunder på denne måde. Så ændrer vi anmodningstypen til GET og sender anmodningen til serveren:

Oprettelse af et projekt
I dette afsnit vil vi oprette en lille RESTful-applikation ved hjælp af Spring Boot. Vores applikation vil implementere CRUD-operationer (Create, Read, Update, Delete) på kunderne fra eksemplet i den foregående del af oversigten. For at starte opretter vi et nyt Spring Boot-program via menuen: Filer -> Nyt -> Projekt... I det vindue, der åbnes, skal du vælge Spring Initializr og angive Project SDK:




RestExampleApplication
) til os. Sådan ser de ud:
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);
}
}
Oprettelse af REST-funktionalitet
Vores applikation er et kundestyringssystem. Så den første ting, vi skal gøre, er at oprette en kundeentitet. Det vil være en POJO-klasse (almindeligt gammelt Java-objekt). Opret enmodel
pakke inde i com.codegym.lessons.rest_example
pakken. Inde i model
pakken skal du oprette 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;
}
}
Tjenesten vil implementere CRUD operationer på kunder. Det næste trin er at skabe en service, der vil implementere disse operationer. com.codegym.lessons.rest_example
Opret en pakke i service
pakken. Og inden i det, opret en CustomerService
grænseflade. Her er grænsefladekoden med kommentarer:
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);
}
Dernæst skal vi implementere denne grænseflade. Nu Map<Integer, Customer>
vil en gemme vores kunder. Kortets nøgler vil være kunde-id'erne, og værdierne vil være kunderne selv. Dette gøres for ikke at overbelaste dette eksempel med detaljerne ved at arbejde med en rigtig database. Men i fremtiden vil vi være i stand til at skrive en anden implementering af grænsefladen, som gør det muligt at oprette forbindelse til en rigtig database. I service
pakken skal du oprette en implementering af grænsefladen 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;
}
}
Annoteringen @Service
fortæller foråret, at denne klasse er en tjeneste. Dette er en speciel type klasse, der implementerer noget forretningsapplikationslogik. Efterfølgende, takket være denne annotation, vil Spring bruge afhængighedsinjektion til at give os en forekomst af denne klasse alle de steder, hvor den er nødvendig. Nu er det tid til at oprette en controller. Dette er en speciel klasse, hvor vi implementerer logikken til behandling af klientanmodninger sendt til slutpunkter (URI'er). For at gøre alt dette klarere, vil vi oprette denne klasse trinvist. Først skal du oprette selve klassen og tilføje en afhængighed af CustomerService
:
@RestController
public class CustomerController {
private final CustomerService customerService;
@Autowired
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
}
Lad os forklare annotationerne: @RestController fortæller Spring, at denne klasse er en REST-controller. Med andre ord implementerer denne klasse logikken til behandling af klientanmodninger. @Autowired fortæller Spring, at der skal tilføjes en afhængighed her. Vi videregiver CustomerService
grænsefladen til konstruktøren. Tidligere markerede vi implementeringen af denne tjeneste med annoteringen @Service
, og nu vil Spring være i stand til at videregive en instans af denne implementering til controllerens konstruktør. Dernæst vil vi implementere hver controller-metode til håndtering af CRUD-operationer. Lad os starte med oprettelsesoperationen. For at gøre dette skriver vi en create
metode:
@PostMapping(value = "/customers")
public ResponseEntity<?> create(@RequestBody Customer customer) {
customerService.create(customer);
return new ResponseEntity<>(HttpStatus.CREATED);
}
Lad os analysere denne metode: @PostMapping(value = "/customers")
betyde, at denne metode behandler POST-anmodninger sendt til adressen "/kunder". Metoden returnerer en ResponseEntity<?>
. A ResponseEntity
er en særlig klasse til returnering af svar. Senere vil vi bruge den til at returnere en HTTP-statuskode til klienten. Metoden har en @RequestBody Customer customer
parameter. Værdien af denne parameter kommer fra anmodningsteksten. Anmærkningen @RequestBody
angiver dette. Inde i metodens krop kalder vi metoden create()
på den tidligere oprettede service og videregiver den, som kundecontrolleren modtog i parametrene. Derefter returnerer vi statussen "201 Oprettet" ved at oprette et nyt ResponseEntity
objekt og sende det tilsvarende HttpStatus
enum-felt til det. Dernæst implementerer viread
operation: Først implementerer vi operationen for at få en liste over alle tilgængelige kunder:
@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);
}
Lad os dykke ned i: @GetMapping(value = "/customers")
— alt her ligner @PostMapping
annoteringen, men nu behandler vi GET-anmodninger. Denne gang returnerer vi en ResponseEntity<List<Customer>>
, og udover en HTTP-status returnerer vi også et svarlegeme, som vil være listen over kunder. I Springs REST-controllere er alt POJO-objekter og samlinger af POJO-objekter, som returneres som svarlegemer og automatisk serialiseres til JSON, medmindre andet er angivet. Dette passer os perfekt. Inde i metoden bruger vi vores service til at få en liste over alle kunder. Dernæst, hvis listen ikke er null og ikke tom, så bruger viResponseEntity
klasse for at returnere listen over kunder og HTTP-statuskoden "200 OK". Ellers returnerer vi blot HTTP-statuskoden "404 Not Found". Nu vil vi implementere muligheden for at få en kunde ved at bruge dens 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);
}
En ny ting her er stivariablen. Variablen er defineret i URI'en: value = "/customers/{id}"
. Vi angiver det med krøllede seler. Og vi modtager det som en int
metodeparameter ved hjælp af @PathVariable(name = "id")
annoteringen. Denne metode accepterer anmodninger sendt til URI'er i formen /customers/{id}
, hvor {id}
repræsenterer enhver numerisk værdi. Denne værdi overføres efterfølgende via int id
variablen til metodeparameteren. I kroppen får vi genstanden Customer
ved hjælp af vores service og den modtagne id
. Og så, analogt med listen, returnerer vi enten "200 OK"-statussen og Customer
selve objektet, eller blot "404 Not Found"-statussen, hvis systemet ikke har nogen kunde med det id
. Vi mangler stadig at implementere to operationer: Opdater og slet. Her er koden til disse metoder:
@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);
}
Der er intet væsentligt nyt i disse metoder, så vi springer den detaljerede beskrivelse over. Det eneste der er værd at nævne er, at update()
metoden håndterer PUT-anmodninger ( @PutMapping
annotation), og delete()
metoden håndterer DELETE-anmodninger ( DeleteMapping
annotation). Her er den fulde kode til controlleren:
@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);
}
}
Som et resultat er strukturen af vores projekt som følger: 
Lancering og test
For at starte vores applikation skal du bare køremain()
metoden i RestExampleApplication
klassen. Men for at teste RESTful webtjenester, er vi nødt til at downloade yderligere software. Faktum er, at GET-anmodninger er ret enkle at sende fra en almindelig browser, men en almindelig browser kan ikke sende POST-, PUT- og DELETE-anmodninger. Bare rolig: du kan bruge et program kaldet Postman til at sende alle HTTP-anmodninger. Du kan downloade den her . Efter at have downloadet og installeret Postman, begynder vi at teste vores applikation. For at gøre dette skal du åbne programmet og oprette en ny anmodning: 



Resumé
Tillykke! Vi har dækket REST tilstrækkeligt. Der var en stor mængde materiale, men forhåbentlig var det nyttigt for dig:-
Vi lærte, hvad REST er.
-
Vi lærte om, hvordan REST opstod.
-
Vi talte om begrænsningerne og principperne bag denne arkitektoniske stil:
- klient-server arkitektur
- statsløs
- caching
- ensartet interface
- lag
- kode efter behov (valgfrit)
-
Vi undersøgte fordelene ved REST
-
Vi undersøgte i detaljer, hvordan serveren og klienten interagerer med hinanden via HTTP-protokollen.
-
Vi har set nærmere på anmodninger og svar. Vi dissekere deres bestanddele.
-
Endelig fik vi lidt praktisk erfaring ved at skrive vores egen lille RESTful-applikation ved hjælp af Spring Boot. Og vi lærte endda at teste det ved hjælp af Postman.
Lektier
Prøv følgende:- Følg beskrivelsen ovenfor, opret dit eget Spring Boot-projekt og implementer den samme logik som i lektionen. Gentag alt nøjagtigt.
- Start applikationen.
- Download og konfigurer Postman (eller ethvert andet værktøj til at sende anmodninger, for eksempel curl).
- Test POST- og GET-anmodninger på samme måde som beskrevet i lektionen.
- Test selv PUT- og DELETE-anmodninger.
GO TO FULL VERSION