-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path01_java_rest_L_intro.qmd
More file actions
401 lines (309 loc) · 16.2 KB
/
Copy path01_java_rest_L_intro.qmd
File metadata and controls
401 lines (309 loc) · 16.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
---
title: "Architecture et Services Web RESTful"
description: |
Introduction aux APIs RESTful (REpresentational State Transfer).
Principes, concepts et bonnes pratiques des services web REST.
Implémentation avec Jakarta RESTful Web Services.
categories:
- Java
- Lecture
- RestFul
- Web Services
provide_notes: true
provide_slides: true
jupyter: java
echo: true
output: true
execute:
error: true
---
```{java}
//| echo: false
//| output: asis
%%shell
PROVIDER="github"
REPO="ebpro/sample-jaxrs"
BRANCH="develop"
gitpull.sh --provider github \
--branch ${BRANCH} ${REPO} \
--message "Les exemples suivants sont accessibles dans le dépôt :"
```
```{java}
//| echo: false
//| output: false
%%shell
PROVIDER="github"
REPO="ebpro/sample-jaxrs"
BRANCH="develop"
source get_src_dir.sh ${PROVIDER} ${REPO}
cd ${SRC_DIR}
./mvnw --quiet verify
```
```{java}
//| echo: false
//| output: false
%jars "/home/jovyan/work/materials/github/ebpro/sample-jaxrs/target/sample-jaxrs-*-withdependencies.jar";
```
::: {.content-visible when-profile="slides"}
## Objectifs
:::
Ce document présente les service Web REST en général et par la pratique en Java.
Il s'appuie sur un exemple simple d'application : [https://github.com/ebpro/sample-jaxrs](https://github.com/ebpro/sample-jaxrs) qui servira à illustrer les notions et sera étudiée en détail dans la partie pratique.
::: {.content-visible when-profile="slides"}
## L'Approche REST (REpresentational State Transfer)
:::
::: {.content-visible when-profile="notes"}
L’idée générale de l'approche REST (REpresentational State Transfer) pour construire une interface de programmation (API) est d’offrir un accès distant à des ressources via une interface commune construite ~~au dessus de~~ "**en**" http. On parle d'approche RESTful quand l'interface d'une application est conforme à une certaine philosophie (il ne s'agit pas d'une norme).
En particulier, le fait que l'API est sans état côté serveur permet de l'utiliser de façon transparente même en cas de serveur proxy/cache.
:::
::: {.content-visible when-profile="slides"}
- Qu'est-ce que REST ?
- **REpresentational State Transfer**
- Un style architectural pour la conception de services web
- Offre un accès distant à des ressources via HTTP
- Principes de REST
- **Interface uniforme** en HTTP
- **Stateless** : Chaque requête est indépendante
- **Cacheable** : Possibilité de mettre en cache les réponses
- **Client-Server** : Séparation claire des responsabilités
:::
::: {.content-visible when-profile="slides"}
## RESTful
- Une approche conformant à ces principes
- Flexibilité dans la conception des API
- Pas une norme, mais une recommandation d'architecture
:::
::: {.callout-important}
RESTfull est une approche d'API client/serveur suivant la logique de navigation dans un hypermedia. On parle d'[HATEOS](https://restcookbook.com/Basics/hateoas) (Hypermedia As The Engine Of Application State).
:::
::: {.content-visible when-profile="slides"}
## Protocole de communication
:::
Pour définir un protocole de communication, il faut généralement définir :
- un système d'identification (d'adressage) des ressources manipulées,
- un protocole de communication,
- un format d'échange de données éventuellement typées,
- un système de gestion des erreurs.
La logique RESTfull est d'utiliser tout ce que propose HTTP pour écrire une API **en** HTTP.
::: {.content-visible when-profile="notes"}
## L'adressage des ressources
:::
::: {.content-visible when-profile="slides"}
## L'adressage des ressources
:::
::: {.callout-important}
Les ressources (ou ensembles de ressources) de l'application sont identifiées par des [URI](https://tools.ietf.org/html/rfc3986).
Les URL sont une sorte particulière d'URI qui indique un moyen d'accès en plus de les identifier de façon unique.
:::
Il n'y a pas de standard pour les API REST. Il vaut généralement mieux rester simple et cohérent. Quelques pratiques sont utilisées classiquement :
::: {.fragment .fade-in-then-semi-out}
- On utilise des noms (pas des verbes) au pluriel pour les ressources :
```{python}
# Toutes les personnes
http://MyServer/MyApp/Persons
```
:::
::: {.fragment .fade-in-then-semi-out}
- <span class="content-visible" when-profile="notes">La ou les informations qui permettent d'identifier une ressource sont inclues dans l'URL et le plus possible dans le chemin plutôt que dans la Query String (par exemple un identifiant) :</span><span class="content-visible" when-profile="slides">Utilisation du chemin pour inclure "paramètres"</span>
```{python}
# La personne d'identifiant 1
http://MyServer/MyApp/Persons/1
```
:::
::: {.content-visible when-profile="slides"}
### L'adressage des ressources ("Jointures")
:::
- <span class="content-visible" when-profile="notes">On évite les "jointures" dans les chemins. Si on le fait, l'ordre doit être constant et logique (la fin du chemin correspond toujours à la ressource retournée).</span><span class="content-visible" when-profile="slides">Eviter les "jointures".</span>
- Les chiens de la personne 1
- <del>`http://MyServer/MyApp/Persons/1/Dogs`</del>
- `http://MyServer/MyApp/Dogs?master_id=1`
::: {.content-visible when-profile="slides"}
### L'adressage des ressources (Pagination et tri)
:::
- <span class="content-visible" when-profile="notes">On utilise la pagination, le filtrage et le tri (via les Query Strings) pour les requêtes complexes et pour contrôler le volume des données retournées. A noter la possibilité d'utiliser les [Matrix Params](https://www.w3.org/DesignIssues/MatrixURIs.htmlhttps://www.w3.org/DesignIssues/MatrixURIs.html) même s'ils ne sont pas standards.</span><span class="content-visible" when-profile="slides">Pagination</span>
- La deuxième page de personnes en utilisant des pages de 10 personnes.
- `http://MyServer/MyApp/Persons?page=2&page_size=10`
- `http://MyServer/MyApp/Persons;page=2;page_size=10` (avec des
Matrix Params)
- filtre qui trie par ordre decroissant de date de création, puis par titre
- `http://MyServer/MyApp/Persons?page=&page_size=10&sort=name,firstname,-created,title`
- <span class="content-visible" when-profile="notes">Une projection explicite de certains champs des données peut être envisagée dans la ressource.</span><span class="content-visible" when-profile="slides">Projection</span>
- La personne d'identifiant 1 restreinte uniquement à certains champs
- `http://MyServer/MyApp/Persons/1?fields=email,firstname,lastname`
- <span class="content-visible" when-profile="notes">Des éléments "administatifs" peuvent/doivent être proposés (par exemple pour gérer la version d'une API en ajoutant /api/v1, /api/v2, ... au début du chemin).</span><span class="content-visible" when-profile="slides">Version : préfixes `/api/v1`, `/api/v2`, ... </span>
## Le protocole d'échange
::: {.content-visible when-profile="notes"}
::: {.callout-important}
Les actions sur les ressources (identifiées par des URIs) sont associées aux verbes (méthodes) standards du protocole HTTP.
:::
REST s'appuie sur le protocole HTTP (HyperText Transfert Protocol) qui est défini dans les RFC 7230 à 7237. La [RFC 7231](https://tools.ietf.org/html/rfc7231) défini les [méthodes](https://tools.ietf.org/html/rfc7231#page-24) et les [codes de retour](https://tools.ietf.org/html/rfc7231#section-6).
Une méthode est "sure" (_safe_) si elle ne modifie pas l'état de serveur.
Une méthode est idempotente (_idempotent_) si l'effet attendu par des appels multiples est identique à un appel unique de la même requête. Les méthodes sures sont donc idempotentes.
:::
| Verbe HTTP | Utilisation | Contraintes |
|:---:| --- | --- |
| **[GET](https://tools.ietf.org/html/rfc7231#section-4.3.1)** | Accès à une ressource identifiée dans l'URL (il peut s'agir d'une collection). | Safe, Idempotent |
| **[HEAD](https://tools.ietf.org/html/rfc7231#section-4.3.2)** | comme GET mais sans le corps de la requête (seul le header http est retourné). Utile pour savoir si une ressource a changé. | Safe, Idempotent |
| **[POST](https://tools.ietf.org/html/rfc7231#section-4.3.3)** | création d’une ressource sans donner l'identifiant. | |
| **[PUT](https://tools.ietf.org/html/rfc7231#section-4.3.4)** | mise à jour complète d'une ressource identifiée (voire création en donnant l'identifiant). | Idempotent |
| **[DELETE](https://tools.ietf.org/html/rfc7231#section-4.3.5)** | suppression d’une ressource. | Idempotent |
| **[OPTIONS](https://tools.ietf.org/html/rfc7231#section-4.3.7)** | liste les actions possibles sur une ressource. | Safe, Idempotent |
| **[PATCH](https://tools.ietf.org/html/rfc5789)** | [RFC 5789](https://tools.ietf.org/html/rfc5789), mises à jour partielle d'une ressource. | |
::: {.content-visible when-profile="notes"}
Donc en particulier GET ne modifie rien, plusieurs appels à PUT n'ajoutent ou n'appliquent une modification qu'une fois mais plusieurs appels à POST en ajoutent plusieurs.
Attention, certains proxies HTTP peuvent empêcher certaines actions en dehors de GET et POST (cf. X-HTTP-Method-Override). Cela peut donc conduire à devoir enfreindre les règles ci-dessus.
:::
::: {.content-visible when-profile="slides"}
## Endpoints
:::
Un **endpoint** REST est défini par un verbe HTTP et une URL.
* **Obtenir toutes les personnes** :
* `GET http://MyServer/MyApp/Persons`
* **Obtenir une personne précise par identifiant** :
* `GET http://MyServer/MyApp/Persons/1`
* **Obtenir toutes les personnes entre 7 et 16ans** (avec un filtre) :
* `GET http://MyServer/MyApp/Persons?ageMin=7&ageMax=16`
* **Supprimer toutes personnes**
* `DELETE http://MyServer/MyApp/Persons`
* **Supprimer une personne**
* `DELETE http://MyServer/MyApp/Persons/1`
## La représentation des ressources
::: {.callout-important}
Les resources sont généralement représentées et échangées à l'aide de langages autodescriptifs comme XML ou JSON.
:::
Par exemple, une personne peut être présentée :
:::: {.columns}
::: {.column width="50%"}
```xml
<?xml version='1.0'?>
<person id='1'>
<lastname>Doe</lastname>
<firstname>John</firstname>
</person>
```
:::
::: {.column width="50%"}
```json
{
"person": {
"-id": 1,
"lastname": "Doe",
"firstname": "John"
}
}
```
:::
::::
::: {.content-visible when-profile="notes"}
Les types de données envoyées ou attendues sont indiqués dans l'entête de la requête HTTP par `Content-Type:` et `Accept:`. Pour cela, on utilise les Internet Media Types (ex MIME Type - Multipurpose Internet Mail Extensions). Il s'agit d'une liste standard de formats et de sous-formats d'échange de données (text/plain, text/xml, application/json, ...).
:::
::: {.content-visible when-profile="slides"}
- Les types de données envoyées ou attendues sont spécifiés dans l'entête de la requête HTTP par les champs `Content-Type:` et `Accept:`
- Cette spécification utilise les Internet Media Types (ex. MIME Type - Multipurpose Internet Mail Extensions)
- Il s'agit d'une liste standard de formats et sous-formats d'échange de données, incluant des exemples tels que text/plain, text/xml, application/json, ...
:::
::: {.content-visible when-profile="slides"}
### Exemple de graphe
:::
L'exemple ci-dessous sérialise des objets Java qui représente un auteur et un livre en JSON et en XML. (Le détail est expliqué plus loin).
```{java}
//| output: true
//| echo: false
import fr.univtln.bruno.samples.jaxrs.model.Library;
import fr.univtln.bruno.samples.jaxrs.model.Library.Author;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Marshaller;
//Création d'un auteur et d'un livre en Java
Library.demoLibrary.removesAuthors();
Author author1 = Library.Author.builder().firstname("Joshua").name("Bloch").build();
Library.demoLibrary.addAuthor(author1);
Library.demoLibrary.addBook(Library.Book.builder().title("Effective Java (English Edition)").authors(Set.of(author1)).build());
//Transformation en JSON
ObjectMapper objectMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
//Transformation en XML
Marshaller marshaller = JAXBContext.newInstance(Library.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
marshaller.marshal(Library.demoLibrary, sw);
//Affichage
render("```json\n"+objectMapper.writeValueAsString(Library.demoLibrary)+"\n```\n\n"+
"---\n"+
"```xml\n"+sw.toString()+"\n```", "text/markdown");
```
## Les code de retours
::: {.content-visible when-profile="notes"}
Le protocole HTTP défini un ensemble de [codes de retours](https://tools.ietf.org/html/rfc7231#section-6). Il est donc possible d'utiliser ces codes standards comme code de retour pour indiquer comment l'opération a réussi ou pourquoi elle a échoué.
:::
::: {.callout-important}
Le code de retour des méthode est un code HTTP. Il est indiqué de façon standard dans l'entête de la réponse et peut être répété dans le contenu si une enveloppe est proposée.
:::
::: {.content-visible when-profile="notes"}
- Le code de retour des méthodes est un code HTTP.
- Il est indiqué de façon standard dans l'entête de la réponse.
- Il peut être répété dans le contenu si une enveloppe est proposée.
:::
::: {.content-visible when-profile="slides"}
### Succès
:::
|Code| Signification | Usage |
|--- | --- | --- |
|200| Ok | Requête traitée avec succès.|
|201| Created | Nouvelle ressource créée.|
|204| No Content | Pas de contenu, pas exmple lors d'une requête DELETE réussie.|
|206| Partial Content | Seulement une partie de résultat est retourné par exemple en cas de pagination (non explicite). |
|304| Not Modified | Utilisation du cache possible. |
::: {.content-visible when-profile="slides"}
### Echec
:::
|Code| Signification | Usage |
|--- | --- | --- |
|400| Bad Request | La requête est invalide et ne peut pas être traitée par le serveur. |
|401| Unauthorized | La requête nécessite que le client soit authentifié. |
|403| Forbidden | Le client est authentifié mais l’utilisateur n’est pas autorisé à accéder à cette ressource. |
|404| Not Found | La ressource demandée n’existe pas. |
|500| Internal Server Error | C'est une erreur générique de fonctionnement, elle devrait toujours être accompagnée d'une description |
## Un échange REST = un échange HTTP
::: {.callout-important}
Un échange d'une API REST correspond donc exactement à un échange http.
:::
::: {.content-visible when-profile="notes"}
- Une requête HTTP
- composée d'un verbe, d'une URL, de la version de HTTP utilisée, d'un en-tête (une liste de couples `nom:valeur` par exemple `Content-Type: text/xml`) et un corps éventuellement vide (les données envoyées).
- Une réponse HTTP
- composée d'un code de retour, de meta données dans l'en-tête et d'un corps qui contient les données. Le corps est éventuellement encapsulé dans une "enveloppe" qui reprend les meta données pour faciliter leur traitement.
:::
::: {.content-visible when-profile="slides"}
- Requête HTTP
- Verbe, URL, version HTTP, en-tête, corps (éventuellement vide).
- Réponse HTTP
- Code de retour, métadonnées dans l'en-tête, corps (éventuellement encapsulé).
:::
## Quelques exemples complets
Requête de création d'une personne :
```http
POST http://MyServer:8080/MyApp/Persons/
Host: MyServer:8080
Content-Type: application/json; charset=utf-8
Content-Length: 36
{"lastname": "Doe",
"firstname": "John"}
```
Requête de modification d'une personne (`id` dans l'URL):
```http
PUT http://MyServer:8080/MyApp/Persons/1
Host: MyServer:8080
Content-Type: application/json; charset=utf-8
Content-Length: 12
{"age":"18"}
```
## Utilisation
Une requête REST peut être envoyée par programmation ou en utilisant un programme dédié comme [curl](https://curl.se/) en ligne de commande, [`postman`](https://www.postman.com/) pour chrome ou [`RestClient`](https://addons.mozilla.org/fr/firefox/addon/restclient/) pour firefox.
Regardez les options de la commande `curl` pour réaliser des requêtes HTTP.
```{java}
%%shell
curl -H "Accept: application/json" \
-H "User-Agent: Demo Script" \
-X GET "https://nominatim.openstreetmap.org/search?q=Universit%C3%A9+de+Toulon&format=json&limit=1"
```