Dit is het laatste deel van ons overzicht van REST. In de vorige delen hebben we het gehad over:
Klik op de knop "Volgende". Geef in het volgende venster "Maven Project" op als het projecttype, specificeer de "Groep" en "Artefact":
Klik op de knop "Volgende". In het volgende venster moeten we de Spring Framework-componenten selecteren die nodig zijn voor het project. Voor ons is Spring Web voldoende:
Klik op de knop "Volgende". Nu hoeft u alleen nog de naam van het project en de locatie ervan in het bestandssysteem aan te geven:
Klik op de knop "Voltooien". Het project is gemaakt en nu kunnen we de structuur ervan zien:
IDEA heeft een Maven-implementatiedescriptor (pom.xml) en de hoofdklasse van de toepassing (
Klik op de knop "Nieuw" in de linkerbovenhoek. Selecteer vervolgens "Verzoek":
Geef het vervolgens een naam en sla het op. Laten we nu proberen een POST-verzoek naar de server te sturen en de eerste klant aan te maken:
Zo creëren we meerdere klanten. Vervolgens wijzigen we het verzoektype in GET en sturen we het verzoek naar de server:

Een project maken
In deze sectie zullen we een kleine RESTful-applicatie maken met behulp van Spring Boot. Onze applicatie zal CRUD-bewerkingen (Create, Read, Update, Delete) implementeren op de klanten uit het voorbeeld in het vorige deel van het overzicht. Om te beginnen maken we een nieuwe Spring Boot-toepassing via het menu: Bestand -> Nieuw -> Project... Selecteer in het geopende venster Spring Initializr en specificeer de Project SDK:




RestExampleApplication
) voor ons gegenereerd. Zo zien ze eruit:
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>
RestVoorbeeldToepassing:
@SpringBootApplication
public class RestExampleApplication {
public static void main(String[] args) {
SpringApplication.run(RestExampleApplication.class, args);
}
}
REST-functionaliteit maken
Onze applicatie is een klantbeheersysteem. Het eerste dat we dus moeten doen, is een klantentiteit maken. Het wordt een POJO-klasse (gewoon oud Java-object). Maak eenmodel
pakket binnen het com.codegym.lessons.rest_example
pakket. Maak in het model
pakket het volgende aan 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;
}
}
De service zal CRUD-bewerkingen op klanten implementeren. De volgende stap is het maken van een service die deze bewerkingen zal implementeren. Maak in het com.codegym.lessons.rest_example
pakket een service
pakket aan. En maak daarbinnen een CustomerService
interface. Hier is de interfacecode met commentaar:
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);
}
Vervolgens moeten we deze interface implementeren. Nu Map<Integer, Customer>
zal een winkel onze klanten. De sleutels van de kaart zijn de klant-ID's en de waarden zijn de klanten zelf. Dit is gedaan om dit voorbeeld niet te overladen met de details van het werken met een echte database. In de toekomst zullen we echter een andere implementatie van de interface kunnen schrijven, die het mogelijk zal maken om verbinding te maken met een echte database. service
Maak in het pakket een implementatie van de CustomerService
interface:
@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;
}
}
De @Service
annotatie vertelt de lente dat deze les een service is. Dit is een speciaal type klasse dat bepaalde logica van bedrijfstoepassingen implementeert. Vervolgens zal Spring, dankzij deze annotatie, afhankelijkheidsinjectie gebruiken om ons een instantie van deze klasse te geven op alle plaatsen waar deze nodig is. Nu is het tijd om een controller te maken. Dit is een speciale klasse waarin we de logica implementeren voor het verwerken van klantverzoeken die naar eindpunten (URI's) worden verzonden. Om dit allemaal duidelijker te maken, zullen we deze klasse stapsgewijs maken. Maak eerst de klasse zelf en voeg een afhankelijkheid toe van CustomerService
:
@RestController
public class CustomerController {
private final CustomerService customerService;
@Autowired
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
}
Laten we de annotaties uitleggen: @RestController vertelt Spring dat deze klasse een REST-controller is. Met andere woorden, deze klasse implementeert de logica voor het verwerken van klantverzoeken. @Autowired vertelt Spring dat hier een afhankelijkheid moet worden toegevoegd. We geven de CustomerService
interface door aan de constructor. Eerder hebben we de implementatie van deze service gemarkeerd met de @Service
annotatie, en nu kan Spring een exemplaar van deze implementatie doorgeven aan de constructor van de controller. Vervolgens zullen we elke controllermethode implementeren voor het afhandelen van CRUD-bewerkingen. Laten we beginnen met de create-bewerking. Om dit te doen, schrijven we een create
methode:
@PostMapping(value = "/customers")
public ResponseEntity<?> create(@RequestBody Customer customer) {
customerService.create(customer);
return new ResponseEntity<>(HttpStatus.CREATED);
}
Laten we deze methode analyseren: @PostMapping(value = "/customers")
bedoel dat deze methode POST-verzoeken verwerkt die naar het adres "/customers" zijn verzonden. De methode retourneert een ResponseEntity<?>
. A ResponseEntity
is een speciale klasse voor het retourneren van antwoorden. Later zullen we het gebruiken om een HTTP-statuscode terug te sturen naar de client. De methode heeft een @RequestBody Customer customer
parameter. De waarde van deze parameter is afkomstig van de aanvraaginstantie. De @RequestBody
annotatie geeft dit aan. Binnen de body van de methode roepen we de create()
methode aan op de eerder gemaakte service en geven deze door aan de klantcontroller die is ontvangen in de parameters. Vervolgens geven we de status "201 Gemaakt" terug door een nieuw ResponseEntity
object te maken en het overeenkomstige HttpStatus
opsommingsveld eraan door te geven. Vervolgens implementeren we deread
operatie: Eerst zullen we de operatie implementeren om een lijst van alle beschikbare klanten te krijgen:
@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);
}
Laten we eens kijken: @GetMapping(value = "/customers")
— alles is hier vergelijkbaar met de @PostMapping
annotatie, maar nu verwerken we GET-verzoeken. Deze keer retourneren we een ResponseEntity<List<Customer>>
, en naast een HTTP-status retourneren we ook een antwoordtekst, die de lijst met klanten zal zijn. In de REST-controllers van Spring zijn alles POJO-objecten en verzamelingen van POJO-objecten, die worden geretourneerd als antwoordlichamen en automatisch worden geserialiseerd in JSON, tenzij anders aangegeven. Dit past perfect bij ons. Binnen de methode gebruiken we onze service om een lijst van alle klanten te krijgen. Als de lijst vervolgens niet null en niet leeg is, gebruiken we deResponseEntity
class om de lijst met klanten en de HTTP-statuscode "200 OK" te retourneren. Anders retourneren we gewoon de HTTP-statuscode "404 Not Found". Nu gaan we de mogelijkheid implementeren om een klant zijn ID te laten gebruiken:
@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);
}
Een nieuw ding hier is de padvariabele. De variabele wordt gedefinieerd in de URI: value = "/customers/{id}"
. We geven het tussen accolades aan. En we ontvangen het als een int
methodeparameter met behulp van de @PathVariable(name = "id")
annotatie. Deze methode accepteert verzoeken die naar URI's worden verzonden in de vorm /customers/{id}
, waarbij {id}
staat voor een numerieke waarde. Deze waarde wordt vervolgens via de int id
variabele doorgegeven aan de methodeparameter. In het lichaam krijgen we het Customer
object met behulp van onze service en het ontvangen id
. En dan, naar analogie met de lijst, geven we ofwel de status "200 OK" en het Customer
object zelf terug, of gewoon de status "404 Niet gevonden" als het systeem daar geen klant mee heeft id
. We moeten nog twee bewerkingen uitvoeren: bijwerken en verwijderen. Hier is de code voor deze methoden:
@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);
}
Er is niets wezenlijk nieuws aan deze methoden, dus we zullen de gedetailleerde beschrijving overslaan. Het enige dat het vermelden waard is, is dat de update()
methode PUT-verzoeken ( annotatie) afhandelt @PutMapping
, en de delete()
methode DELETE-verzoeken ( DeleteMapping
annotatie). Hier is de volledige code voor de controller:
@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);
}
}
De opbouw van ons project ziet er dan ook als volgt uit: 
Lanceren en testen
Om onze applicatie te starten, voert u gewoon demain()
methode in de RestExampleApplication
klas uit. Maar om RESTful-webservices te testen, moeten we aanvullende software downloaden. Feit is dat GET-verzoeken vrij eenvoudig te verzenden zijn vanuit een gewone browser, maar een gewone browser kan geen POST-, PUT- en DELETE-verzoeken verzenden. Maak je geen zorgen: je kunt een programma genaamd Postman gebruiken om HTTP-verzoeken te verzenden. Je kunt het hier downloaden . Na het downloaden en installeren van Postman beginnen we met het testen van onze applicatie. Open hiervoor het programma en maak een nieuwe aanvraag aan: 



Samenvatting
Gefeliciteerd! We hebben REST voldoende afgedekt. Er was een grote hoeveelheid materiaal, maar hopelijk was het nuttig voor u:-
We hebben geleerd wat REST is.
-
We leerden hoe REST is ontstaan.
-
We spraken over de beperkingen van en principes achter deze bouwstijl:
- client-server-architectuur
- staatloos
- cachen
- uniforme interface
- lagen
- code op aanvraag (optioneel)
-
We hebben de voordelen van REST onderzocht
-
We hebben in detail onderzocht hoe de server en client met elkaar communiceren via het HTTP-protocol.
-
We hebben de verzoeken en reacties onder de loep genomen. We hebben hun samenstellende delen ontleed.
-
Ten slotte hebben we wat praktische ervaring opgedaan door onze eigen kleine RESTful-applicatie te schrijven met behulp van Spring Boot. En we hebben zelfs geleerd hoe we het kunnen testen met Postman.
Huiswerk
Probeer het volgende:- Maak volgens de bovenstaande beschrijving uw eigen Spring Boot-project en implementeer dezelfde logica als in de les. Herhaal alles precies.
- Start de applicatie.
- Download en configureer Postman (of een andere tool voor het verzenden van verzoeken, bijvoorbeeld curl).
- Test POST- en GET-verzoeken op dezelfde manier als beschreven in de les.
- Test zelf PUT- en DELETE-verzoeken.
GO TO FULL VERSION