I. Liens utiles▲
Les spécifications RAMLRESTful API Modeling Language : https://github.com/raml-org/raml-spec/blob/master/versions/raml-10/raml-10.md
Un guide de bonnes pratiques pour la mise en œuvre d'APIApplication Programming Interface RESTRepresentational State Transfer : http://blog.octo.com/designer-une-api-rest/
II. Configuration logicielle▲
- Windows 10
- ATOMÂ 1.10.2
- API Workbench 0.8.38
- API Console 3.0.8
- Tomcat 8.5.5
III. Qu'est-ce que RAMLÂ ?▲
RAMLRESTful API Modeling Language est un langage structuré basé sur YAML (un format de représentation de données) permettant de décrire les données en entrée et en sortie des services web RESTRepresentational State Transfer.
Avec RAMLRESTful API Modeling Language, il est possible de décrire des ressources, des méthodes et d'autres aspects de conception telle que la sécurité.
RAMLRESTful API Modeling Language permet de décrire les APIApplication Programming Interface RESTRepresentational State Transfer et de faciliter la communication entre développeurs. Il permet d'avoir un aperçu de la conception à un stade précoce, ce qui permet d'anticiper les changements à effectuer dans le programme et de communiquer des idées aux membres de l'équipe avant de démarrer le développement, ce qui favorise leur implication dans le processus de développement.
En outre, RAMLRESTful API Modeling Language intègre deux des meilleures pratiques des développeurs : les « design patterns » et la réutilisabilité du code.
RAMLRESTful API Modeling Language vous permet de profiter des avantages des bibliothèques, du polymorphisme, de la réutilisation des traitements (via les « traits » - Voir le paragraphe IV.B.10 Le nœud « traits »), de la réutilisation des ressources (via les « resourceType » - Voir le paragraphe IV.B.11 Le nœud « resourceTypes ») pour vous assurer que votre APIApplication Programming Interface reste cohérente, tout en vous permettant d'économiser beaucoup de temps.
IV. Les bases de RAML▲
IV-A. La racine des spécifications RAML▲
On peut assimiler les spécifications d'une APIApplication Programming Interface RESTRepresentational State Transfer en RAMLRESTful API Modeling Language à un fichier texte d'extension « .raml ». Le contenu de ce fichier est composé d'un ensemble de nœuds qui définissent la racine des spécifications.
Pour définir une APIApplication Programming Interface RESTRepresentational State Transfer basique en RAMLRESTful API Modeling Language, nous pouvons partir d'un squelette de fichier tel que nous le présentons ci-dessous. Dans ce squelette, chacune des lignes commence par ce qui est appelé un « nœud » et qui peut être assimilé à une balise en XML :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
#%RAML 1.0
title
:
description
:
version
:
baseUri
:
baseUriParameters
:
protocols
:
mediaType
:
documentation
:
annotationTypes
:
traits
:
resourceTypes
:
types
:
/vehicules
:
#Exemple de nom de ressource
/conducteurs
:
#Exemple de nom de ressource
Afin de bien assimiler les rouages de RAMLRESTful API Modeling Language, nous renseignerons, dans ce tutoriel, les nœuds ci-dessus les uns après les autres tout en expliquant leur utilité.
Dans le code ci-dessus, #%RAML 1.0 indique que le document est un fichier RAMLRESTful API Modeling Language dont la version de ce langage est 1.0. Cette ligne constituera toujours la première ligne du fichier.
La racine du document commence véritablement à la seconde ligne. Dans le paragraphe suivant, nous expliquerons la raison d'être de chacun de ces nœuds en l'illustrant par un exemple.
Puis, plus loin, nous verrons, la manière de compléter ces nœuds par d'autres nœuds dans le but de manipuler des « design pattern » ou encore de faire appel à des bibliothèques externes et, à fortiori, de réutiliser le code.
IV-B. Description des nÅ“uds▲
À partir du squelette de fichier présenté ci-dessus, nous allons créer, petit à petit, une APIApplication Programming Interface RAMLRESTful API Modeling Language en présentant l'utilité de chacun des nœuds et en les complétant avec les valeurs qu'il est possible de leur affecter.
IV-B-1. Le nÅ“ud « title »▲
Il s'agit d'un nœud obligatoire qui permet de nommer l'APIApplication Programming Interface.
Exemple :
title
:
API de gestion d′un parc automobile
IV-B-2. Le nÅ“ud « description »▲
Il s'agit d'un nœud facultatif qui permet de décrire l'APIApplication Programming Interface. La description peut être rédigée au format markdown.
Exemple :
description
:
API permettant de gérer les opérations CRUD ( create, read, update, delete) relatives la gestion d′un parc de véhicules
IV-B-3. Le nÅ“ud « version »▲
Il s'agit d'un nœud facultatif qui permet de préciser la version de l'APIApplication Programming Interface (v1, v1.2…).
Exemple :
version
:
v1
Afin de respecter les conventions, ce numéro de version devra figurer au plus haut niveau du chemin de l'URIUniform Resource Identifier.
Exemple : https://dijon.garage.com/v1/vehicules
IV-B-4. Le nÅ“ud « baseUri »▲
Il s'agit d'un nœud facultatif qui précise le endpoint du service web RESTRepresentational State Transfer.
Il renseigne sur l'URL de base qui permet l'accès aux différentes ressources. Cette URL de base peut être une URL classique (de la forme « /XXXX… » ou « http://XXXX… » ou « https://XXXX… ») ou un « template » (du type « /{yyyy}… » ou « http://XXXX/{yyyy}… » ou « https://XXXX/{yyyy}… ») dans lequel le paramètre qui figure entre les accolades « {} » est défini obligatoirement dans le nœud « baseUriParameters » présenté dans le paragraphe suivant.
Dans le cas où un ou plusieurs caractères « / » sont placés à la fin de l'URL de base, ces caractères seront ignorés.
Par exemple :
Dans l'exemple ci-dessous, les chemins absolus aux ressources « vehicules » et « voitures » seront respectivement et  :
2.
3.
baseUri
:
https
:
//{
ville}
.garage.com/{
release}
/
/vehicules
:
/voitures
:
Ce qui revient donc à écrire :
2.
3.
baseUri
:
https
:
//{
ville}
.garage.com/{
release}
/vehicules
:
/voitures
:
Remarquez l'absence du « / » à la fin de l'URIUniform Resource Identifier dans le second exemple.
IV-B-5. Le nÅ“ud « baseUriParameters »▲
Il s'agit d'un nœud facultatif qui définit les paramètres nommés utilisés dans le nœud « baseUri » si ce dernier définit un « template » (comme nous l'avons vu ci-dessus).
Par exemple :
2.
3.
4.
5.
6.
7.
baseUri
:
https
:
//{
ville}
.garage.com/{
version}
baseUriParameters
:
ville
:
description
:
Sous-
domaine auquel l′API est accessible.
enum
:
[
paris, dijon]
release
:
description
:
Version de l′API
Nous voyons, dans l'exemple ci-dessus, qu'il est fait référence à deux « templates » dans le nœud « baseUri » qui sont « ville » et « version ».
Ces deux « templates » sont définis dans le nœud « baseUriParameters ».
IV-B-6. Le nÅ“ud « protocols »▲
Il s'agit d'un nœud facultatif qui définit le protocole supporté par l'APIApplication Programming Interface.
Si ce nœud n'est pas renseigné, seul(s) le/les protocole(s) inclus dans le nœud « baseUri » est/sont utilisé(s).
Si ce nœud est spécifié, ce sont les protocoles énumérés dans ce nœud qui sont pris en compte. Le nœud « protocols » prend comme valeur un tableau non vide de chaînes de caractères dans lequel les chaînes sont « HTTP » et/ou « HTTPS ». Ces valeurs ne sont pas sensibles à la casse.
Exemple :
2.
baseUri
:
https
:
//{
apiDomain}
.garage.com/{
version}
protocols
:
[
HTTP, HTTPS ]
IV-B-7. Le nÅ“ud « mediaType »▲
Il s'agit d'un nœud facultatif qui spécifie le type de média par défaut utilisé dans le « payload », c'est-à -dire dans le « body » de la requête envoyée au service web et dans le « body » de la réponse retournée par le service web.
Ce nœud évite de définir le « mediaType » dans toutes les définitions des ressources, ce qui doit être fait obligatoirement, si vous n'utilisez pas ce nœud, de la manière suivante :
2.
3.
4.
5.
6.
7.
/conducteurs
:
get
:
responses
:
200
:
body
:
application/json
:
type
:
Conducteur[]
Les valeurs les plus couramment utilisées sont « application/json » et « application/xml ». Une ou plusieurs valeurs peuvent être spécifiées dans ce nœud. Dans le cas où plusieurs valeurs sont spécifiées, elles doivent apparaître dans un tableau.
Exemple :
mediaType
:
[
application/json, application/xml ]
Dans l'exemple ci-dessus, on spécifie que les données peuvent être transmises au format « JSON » ou au format « XML ».
Il est néanmoins possible de définir un « mediaType » explicitement dans le « body » d'une requête ou d'une réponse liée à une ressource. Cette définition sera alors prioritaire sur la valeur définie dans le nœud « mediaType ».
Exemple :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
mediaType
:
[
application/json, application/xml ]
types
:
Vehicule
:
...
Conducteur
:
...
/conducteurs
:
get
:
responses
:
200
:
body
:
Conducteur[]
/vehicules
:
post
:
body
:
application/json
:
type
:
Vehicule
Dans l'exemple ci-dessus, la ressource retourne un tableau d'instances de type « Conducteur » () représenté soit en « JSON », soit en « XML » car, étant donné que le « mediaType » n'est pas défini dans la ressource, le « mediaType » par défaut qui s'appliquera sera celui défini dans le nœud « mediaType ».
En revanche, la ressource utilisera la valeur de « mediaType » qui lui est explicitement définie. Par conséquent, la ressource retournera uniquement des données de type « Voiture » au format « JSON ».
IV-B-8. Le nÅ“ud « documentation »▲
Il s'agit d'un nœud facultatif permettant d'écrire ou de faire référence à de la documentation (notice utilisateur ou documentation de référence de l'APIApplication Programming Interface). Le but de ce nœud est de pouvoir se documenter sur le fonctionnement de l'APIApplication Programming Interface et/ou de fournir les contextes techniques et métiers dans lesquels le service web est intégré.
Ce nœud se compose d'une séquence d'un ou plusieurs documents représentés sous forme de « map » contenant deux colonnes (clé, valeur). La clé n'est autre que le titre de la documentation et la valeur est son contenu. Ce contenu peut être un texte brut ou un lien vers une documentation. Dans ce dernier cas, le lien est spécifié par l'utilisation de « !include ». Le contenu peut être formaté en utilisant le langage de balisage « markdown ».
Exemple :
2.
3.
4.
5.
6.
documentation
:
-
title
:
Accueil
content
:
Bienvenue sur la documentation de l′API de gestion de parc automobile
-
title
:
Legal
content
:
!include docs/legal.markdown
IV-B-9. Le nÅ“ud « types »▲
Il s'agit d'un nœud facultatif qui définit les types des données utilisés dans l'APIApplication Programming Interface.
Les types de données peuvent être déclarés :
- Dans le nœud optionnel « types » ;
Il s'agit, dans ce cas, de prévoir la réutilisabilité du type dans différentes ressources. Ainsi, il n'y a pas besoin de définir le même type dans toutes les ressources qui l'utilisent. C'est la solution à adopter afin de factoriser le code et ainsi d'alléger la définition des ressources.
Exemple :
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.
types
:
Vehicule
:
type
:
object
properties
:
marque
:
required
:
true
type
:
string
modele
:
required
:
true
type
:
string
categorie
:
required
:
true
type
:
string
places
:
number #Par défaut required: false. Cette syntaxe est équivalente à "places?: number"
prix
:
required
:
true
type
:
number
couleur
:
required
:
true
enum
:
-
Blanc
-
Noir
-
Gris
-
Bordeaux
-
Jaune
-
Bleu
example
:
marque
:
RENAULT
modele
:
Scenic
categorie
:
Monospace
prix
:
31500
couleur
:
Gris
Conducteur
:
type
:
object
properties
:
nom
:
required
:
true
type
:
string
prenom
:
required
:
true
type
:
string
date_naissance
:
required
:
true
type
:
date-
only
example
:
1981
-
05
-
04
example
:
nom
:
Célère
prenom
:
Jacques
date_naissance
:
1981
-
05
-
04t#y#p#e#s#:#
Dans l'exemple ci-dessus, nous définissons deux types (« Vehicule » et « Conducteur ») que nous pouvons, dès lors, utiliser dans les ressources « /vehicules » et « /conducteurs » de la manière suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
/vehicules
:
displayName
:
vehicules
get
:
responses
:
200
:
body
:
application/json
:
type
:
Vehicule[]
post
:
body
:
Vehicule
/conducteurs
:
get
:
responses
:
200
:
body
:
application/json
:
type
:
Conducteur[]
-
au sein de la définition des ressources. Il s'agit de la méthode la moins claire en termes de relecture ;
Exemple :Exemple types2Sélectionnez1.
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./
vehicules
:
get
:
responses
:
200
:
body
:
application/json
:
post
:
body
:
application/json
:
type
:
objectproperties
:
marque
:
stringmodele
:
string categorie:
stringprix
:
numbercouleur
:
enum
:
-
Blanc-
Noir-
Coloréexample
:
|
{
"marque"
:
"RENAULT"
,"modele"
:
"SCENIC"
,"categorie"
:
"Monospace"
,"prix"
:
25000
,"couleur"
:
"Noir"
}
Avec cet exemple, nous voyons dès lors qu'il est impossible de réutiliser le type à moins de le définir à nouveau dans une autre ressource. Par conséquent, la spécification deviendra vite illisible sans compter les oublis si on change le type dans une ressource et pas dans une autre…
- au sein d'une bibliothèque qui sera incluse comme le présente le paragraphe « VIII Création d'une bibliothèque RAML ». L'avantage de l'utilisation d'une bibliothèque est de pouvoir réutiliser nos propres types, « ressourceType » (voir le paragraphe IV.B.11 Le nœud « resourceTypes ») et « traits » (voir le paragraphe IV.B.10 Le nœud « traits ») dans d'autres fichiers RAMLRESTful API Modeling Language (voir le paragraphe VIII Création d'une bibliothèque RAML) ;
Exemple d'inclusion de bibliothèque :
2.
3.
4.
5.
6.
7.
8.
title
:
Garage
uses
:
NewLibrary
:
NewLibrary.raml
version
:
v1
baseUri
:
/garage
types
:
Vehicule
:
type
:
NewLibrary.Vehicule
Nous verrons plus en détail la déclaration de type dans le paragraphe « VI Les types ».
IV-B-10. Le nÅ“ud « traits »▲
Il s'agit d'un nœud facultatif permettant de réutiliser des fonctionnalités dans différentes méthodes (get, post, delete, put).
Ces fonctionnalités peuvent être, par exemple, une description ou des paramètres à fournir à une méthode. Les méthodes qui utilisent un ou plusieurs de ces « traits » héritent de toutes les caractéristiques de ces « traits », c'est-à -dire de l'ensemble des nœuds contenus dans ces « traits ».
P ar exemple, supposons que nous ayons une ressource décrite de la manière suivante :
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.
/vehicules
:
get
:
queryParameters
:
page
:
description
:
Numéro de page à récupérer
type
:
number
required
:
true
example
:
1
par_page
:
description
:
Nombre d′instances récupérées par page
type
:
integer
minimum
:
20
maximum
:
50
default
:
30
example
:
50
prixPlusPetitQue?:
description
:
"Prix plus petit que"
#Le "?" signifie que le paramètre est optionnel
type
:
number
prixPlusGrandQue?:
#Le "?" signifie que le paramètre est optionnel
description
:
Prix plus grand que
type
:
number
secured
:
usage
:
S′applique aux méthodes qui ont besoin d′être sécurisées
description
:
Certaines requêtes requièrent une authentification
headers
:
access_token
:
description
:
Access Token
example
:
5757gh76
required
:
true
responses
:
200
:
body
:
applications/json
:
type
:
Vehicule[]
La méthode get sera donc une requête qui pourra exiger jusqu'à cinq paramètres nommés : « page », « par_page », « prixPlusPetitQue », « prixPlusGrandQue » et « access_token ».
Afin de simplifier la lecture du code et de permettre la réutilisabilité de ces paramètres dans d'autres méthodes (par exemple, on peut envisager que toutes les méthodes « get » de chaque ressource exigent les paramètres de pagination), nous pouvons créer un nœud « traits ».
Les ressources et les types de ressource (voir le paragraphe IV.B.11 Le nœud « resourceTypes » ci-dessous), peuvent aussi utiliser, et donc hériter, de « traits » qui s'appliquent alors à toutes les méthodes de la ressource et de la « resourceType ». Nous verrons également un exemple de cette utilisation de « traits » par les ressources et les « resourceTypes » dans la section IV.B.11 Le nœud « resourceTypes ».
Exemple de « traits » à partir de l'exemple précédent permettant « d'alléger » la méthode « get »  :
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.
traits
:
pagination
:
usage
:
S′applique uniquement aux méthodes get
description
:
Permet de paginer les résultats
queryParameters
:
page
:
description
:
Numéro de page à récupérer
type
:
number
required
:
true
example
:
1
par_page
:
description
:
Nombre d′instances récupérées par page
type
:
integer
minimum
:
20
maximum
:
50
default
:
30
example
:
50
filtre_par_prix
:
usage
:
S′applique uniquement aux méthodes get
description
:
Permet de filtrer les réponses en fonction du prix
queryParameters
:
prixPlusPetitQue?:
description
:
"Prix plus petit que"
#Le "?" signifie que le paramètre est optionnel
type
:
number
prixPlusGrandQue?:
#Le "?" signifie que le paramètre est optionnel
description
:
Prix plus grand que
type
:
number
secured
:
usage
:
S′applique aux méthodes qui ont besoin d′être sécurisées
description
:
Certaines requêtes requièrent une authentification
headers
:
access_token
:
description
:
Access Token
example
:
5757gh76
required
:
true
-
Dans l'exemple ci-dessus, nous avons déplacé les paramètres définis dans la méthode « get » dans un nœud « traits ».
Nous créons trois « traits » :- Le « trait » nommé « pagination » spécifie des paramètres de requête (mot clé « queryParameters ») permettant de préciser, lors de l'appel d'une méthode, le numéro de la page à récupérer ainsi que le nombre d'instances par page. Il accepte deux paramètres en entrée nommés respectivement « page » et « par_page » ;
- Le « trait » nommé « Filtre_par_prix » spécifie des paramètres de requête permettant de filtrer les collections d'instances en fonction du prix. Il accepte deux paramètres en entrée nommés respectivement « prixPlusPetitQue » et « prixPlusGrandQue » ;
- Le « trait » nommé « secured » spécifie un paramètre de sécurité dans les requêtes.
L'utilisation de ces « traits » par une méthode est spécifiée par le mot clé « is » de la manière suivante (par exemple sur la ressource « /vehicules ») :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
/vehicules
:
is
:
[
secured ]
#Les méthodes utilisées par la ressource « /vehicules » seront toutes sécurisées
get
:
is
:
[
filtre_par_prix, pagination ]
responses
:
200
:
body
:
applications/json :
type
:
Vehicule[]
post
:
body
:
application/json
:
type
:
Vehicule
Dans le but de passer des valeurs de paramètre aux « traits » et aux « resourceTypes » (que nous verrons dans le paragraphe suivant) et ainsi permettre d'utiliser des « pattern », il est possible d'utiliser une map dans la déclaration du « trait » ou du « resourceType » comme illustré ci-dessous :
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.
traits
:
pagination
:
usage
:
S′applique uniquement aux méthodes get
description
:
Permet de paginer les résultats
queryParameters
:
page
:
description
:
Numéro de page à récupérer
type
:
number
required
:
true
example
:
1
par_page
:
description
:
Nombre d′instances récupérées par page
type
:
integer
minimum
:
20
maximum
:
50
default
:
30
example
:
50
filtre_par_prix
:
usage
:
S′applique uniquement aux méthodes get
description
:
Permet de filtrer les réponses en fonction du prix
queryParameters
:
prixPlusPetitQue?:
#Le "?" signifie que le paramètre est optionnel
description
:
Prix plus petit que
type
:
number
prixPlusGrandQue?:
#Le "?" signifie que le paramètre est optionnel
description
:
Prix plus grand que
type
:
number
secured
:
usage
:
S′applique aux méthodes qui ont besoin d′être sécurisées
description
:
Certaines requêtes requièrent une authentification
headers
:
<<tokenName>>:
description
:
Exige un <<tokenName>>
valide
/vehicules
:
is
:
[
secured
:
{
tokenName
:
access_token}
]
#Les méthodes utilisées par la ressource « /vehicules » seront toutes sécurisées
displayName
:
vehicules
get
:
responses
:
200
:
body
:
application/json
:
type
:
Vehicule[]
is
:
[
filtre_par_prix, pagination ]
post
:
body
:
application/json
:
type
:
Vehicule
En plus de pouvoir être définis dans des bibliothèques (voir le paragraphe VIII Création d'une bibliothèque RAML), les « traits » peuvent également être définis dans un fichier d'extension « .raml » qui est ensuite importé de la manière suivante dans notre APIApplication Programming Interface :
2.
3.
4.
traits
:
secured
:
!include traits/secured.raml
pagination
:
!include traits/pagination.raml
filtre_par_prix
:
!include traits/filtre_par_prix.raml
IV-B-11. Le nÅ“ud « resourceTypes »▲
Il s'agit d'un nœud facultatif permettant de réutiliser les méthodes (get, post, delete, put), et leurs caractéristiques (response, body, type…), dans plusieurs ressources.
Exemple :
Considérons la ressource « /vehicules » de l'exemple précédent :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
/vehicules
:
is
:
[
secured
:
{
tokenName
:
access_token}
]
#Les méthodes utilisées par la ressource « /vehicules » seront toutes sécurisées
displayName
:
vehicules
get
:
responses
:
200
:
body
:
application/json
:
type
:
Vehicule[]
is
:
[
filtre_par_prix, pagination ]
post
:
body
:
application/json
:
type
:
Vehicule
Il apparaît que les méthodes « get » et « post » pourraient être réutilisées dans d'autres ressources pour peu que ces autres ressources retournent des objets de type « Vehicule ». Dès lors, nous pouvons déclarer ces méthodes sous forme de « resourceType » qui pourraient être appelés par d'autres ressources.
Ceci simplifiera la lecture de la spécification et surtout la réutilisabilité du code :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
resourceTypes
:
Collection
:
get
:
responses
:
200
:
body
:
application/json
:
type
:
Vehicule[]
post
:
body
:
application/json
:
type
:
Vehicule
/vehicules
:
is
:
[
secured ]
#Les méthodes utilisées par la ressource « /vehicules » seront toutes sécurisées
displayName
:
vehicules
type
:
Collection
get
:
is
:
[
filtre_par_prix, pagination ]
Le code ci-dessus permet de définir un type de ressource nommé « Collection » qui possède une méthode « get » ainsi qu'une méthode « post » retournant toutes les deux des objets de type « Vehicule » au format « JSON ».
Les méthodes « get » et « post » de la ressource « /vehicules » sont héritées de la « resourceType » nommée « Collection ».
Les méthodes « put » et « delete » peuvent être définies directement au sein de la ressource ou dans le nœud « resourceTypes » comme nous l'avons fait pour les méthodes « get » et « post ».
Il est possible de factoriser encore un peu plus le code afin qu'il soit réutilisable dans la majorité des ressources. Pour cela, comme pour les « traits », nous pouvons paramétrer les « resourceTypes » comme le présente l'exemple ci-dessous :
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.
resourceTypes
:
Collection
:
get
:
responses
:
200
:
body
:
application/json
:
type
:
<<item>>[]
post
:
body
:
application/json
:
type
:
<<item>>
/vehicules
:
is
:
[
secured ]
#Les méthodes utilisées par la ressource « /vehicules » seront toutes sécurisées
displayName
:
vehicules
type
:
{
Collection
:
{
item :
Vehicule}
}
get
:
is
:
[
filtre_par_prix, pagination ]
/conducteurs
:
displayName
:
conducteurs
type
:
{
Collection
:
{
item :
Conducteur}
}
/{
id}:
put
:
description
:
Met à jour un conducteur dont l′identifiant est fourni dans l′uri
body
:
application/json
:
type
:
Conducteur
delete
:
responses
:
204
:
Dans l'exemple ci-dessus, la « resourceType » nommée « Collection » est utilisée à la fois dans la ressource « /vehicules » et dans la ressource « /conducteurs ». Pour cela, nous avons défini un paramètre nommé « item » dont on doit préciser le type dans la définition de la méthode.
Nous pouvons créer une seconde « resourceTypes » afin de considérer le cas des méthodes « put » et « delete » :
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.
resourceTypes
:
Collection
:
get
:
responses
:
200
:
body
:
application/json
:
type
:
<<item>>[]
post
:
body
:
application/json
:
type
:
<<item>>
Membre
:
put
:
body
:
application/json
:
type
:
<<item>>
delete
:
responses
:
204
:
/conducteurs
:
displayName
:
conducteurs
type
:
{
Collection
:
{
item :
Conducteur}
}
get
:
description
:
Permet de récupérer un ensemble d′objets de type "Conducteur"
post
:
description
:
Permet d′enregistrer un nouveau conducteur
/{
id}:
#On a affaire à une sous-ressource
type
:
{
Membre
:
{
item :
Conducteur}
}
put
:
description
:
Met à jour un conducteur dont l′identifiant est fourni dans l′uri
delete
:
description
:
Supprime un conducteur
Les « resourceTypes » peuvent être définies dans un fichier d'extension « .raml » qui est ensuite importé de la manière suivante :
2.
resourceTypes
:
collection
:
!include resourceTypes/collection.raml
IV-B-12. Le nÅ“ud « annotationTypes »▲
Il s'agit d'un nœud facultatif permettant de compléter les spécifications à l'aide de métadonnées. Ces annotations ne sont pas comprises par le processeur RAMLRESTful API Modeling Language. Néanmoins, elles peuvent être utilisées pour donner plus de détails dans les spécifications. Ces métadonnées pourront ensuite être utilisées pour améliorer la documentation, les tests ou l'exploitation.
Nous verrons, dans ce paragraphe, que les annotations peuvent être de deux types (standard ou personnalisé) et que chaque annotation peut concerner seulement certains types d'éléments de la spécification ou plusieurs.
IV-B-12-a. Les annotations « standards »▲
Il existe des types d'annotations « standards » comme « displayName » et « description » qui peuvent être utilisés dans n'importe quel nœud.
Ces types d'annotations sont utilisés de la manière suivante pour détailler l'utilité d'une ressource, d'une méthode… :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
/vehicules
:
displayName
:
Gestion des vehicules
type
:
Collection
/{
id}:
put
:
description
:
Met à jour un véhicule dont l′identifiant est fourni en paramètre
body
:
application/json
:
type
:
Vehicule
delete
:
responses
:
204
:
Dans l'exemple ci-dessus, l'annotation « displayName » permet de décrire l'utilité de la ressource alors que l'annotation « description », qui peut être écrite en utilisant le langage Markdown permet de décrire l'utilité de la méthode.
IV-B-12-b. Les annotations personnalisées▲
Il est également possible de créer d'autres annotations qui se composent de couples (clé, valeur) comme le présente l'exemple ci-dessous et pour lesquelles il est possible de préciser pour quel type d'entité elles peuvent être utilisées (ressource, méthode…) via l'intermédiaire de l'annotation « allowedTargets ». Cette dernière peut prendre une ou plusieurs valeurs parmi celles référencées sur la page https://github.com/raml-org/raml-spec/blob/master/versions/raml-10/raml-10.md#annotation-target-location.
Ces annotations utilisent le nœud « annotationTypes » comme nous pouvons le voir ci-dessous :
Exemple :
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.
annotationTypes
:
deprecie
:
nil
experimental
:
string? #équivalent à « nil | string »
auteur
:
string #string est le type par défaut
#Deux autres syntaxes possible sont :
#auteur:
# type: string
#ou
#auteur:
niveau_autorisation
:
allowedTargets
:
[
Resource ]
properties
:
niveau
:
enum
:
[
faible, moyen, élevé ]
required
:
true
signature
:
pattern
:
"\\d{3}-\\w{12}"
required
:
true
meta-
resource-
method
:
allowedTargets
:
[
Resource, Method ]
meta-
data
:
allowedTargets
:
TypeDeclaration
types
:
Vehicule
:
(meta-
data):
Sur la déclaration du type "Vehicule"
properties
:
marque
:
type
:
string
required
:
true
(meta-
data):
Sur la déclaration de la "properties"
modele
:
type
:
string
required
:
true
categorie
:
type
:
string
required
:
true
enum
:
-
Citadine
-
berline
-
Monospace
-
SUV
places
:
integer #Par défaut required: true.
prix
:
required
:
false
type
:
number
couleur
:
required
:
true
enum
:
-
Blanc
-
Noir
-
Gris
-
Bordeaux
-
Jaune
-
Bleu
example
:
marque
:
RENAULT
modele
:
Scenic
categorie
:
Monospace
places
:
7
prix
:
31500
couleur
:
Gris
Conducteur
:
properties
:
nom
:
type
:
string
required
:
true
prenom
:
type
:
string
required
:
true
date_naissance
:
type
:
date-
only
required
:
true
example
:
1981
-
05
-
04
example
:
nom
:
Célère
prenom
:
Jacques
date_naissance
:
1981
-
05
-
04
/vehicules
:
is
:
[
secured ]
#Les méthodes utilisées par la ressource « /vehicules » seront toutes sécurisées
displayName
:
Gestion des vehicules
(meta-
resource-
method):
Sur une ressource
type
:
{
Collection
:
{
item :
Vehicule}
}
get
:
description
:
Permet de récupérer un ensemble d′objets de type "Vehicule"
is
:
[
filtre_par_prix, pagination ]
(deprecie):
(experimental):
post
:
description
:
Permet d′enregistrer un nouveau véhicule
/conducteurs
:
displayName
:
Gestion des conducteurs
(niveau_autorisation):
niveau
:
élevé
signature
:
230
-
ghtwvfrs1itr
type
:
{
Collection
:
{
item :
Conducteur}
}
get
:
description
:
Permet de récupérer un ensemble d′objets de type "Conducteur"
(deprecie):
(experimental):
post
:
description
:
Permet d′enregistrer un nouveau conducteur
/{
id}:
#On a affaire à une sous-ressource
type
:
{
Membre
:
{
item :
Conducteur}
}
put
:
description
:
Met à jour un conducteur dont l′identifiant est fourni dans l′uri
delete
:
description
:
Supprime un conducteur
Dans l'exemple ci-dessus, nous avons créé six annotations (« deprecie », « experimental », « auteur », « niveau_autorisation », « meta-resource-method », « meta-data ») afin de montrer la syntaxe des définitions des annotations et comment spécifier qu'une annotation peut être appliquée sur un nœud ou un autre.
Nous voyons par exemple que :
- l'annotation « deprecie », de par son type « nil », n'autorise pas la saisie d'une valeur ;
- l'annotation « experimental », de par son type « string ? » équivalent à « nil | string », peut ne pas être renseignée ou peut contenir une chaîne de caractères ;
- l'annotation « auteur » est forcément de type « string » si elle apparaît ;
- l'annotation « niveau_autorisation » est composée de deux propriétés que sont « niveau » et « signature ». À cause de la présence de l'attribut « allowedTargets » ayant pour valeur un tableau contenant la valeur « Resource », ces deux propriétés ne pourront être utilisées que dans les nœuds ressource.
La propriété « niveau » ne peut prendre que les valeurs « faible », « moyen » ou « élevé » alors que la propriété « signature » ne peut prendre comme valeur qu'une chaîne de caractères dont la composition est définie par l'expression régulière « \\d{3}-\\w{12} ». Cette expression régulière spécifie une chaîne de caractères commençant par trois chiffres suivis du caractère « - » suivis de douze caractères alphanumériques. Les expressions régulières sont basées sur les expressions régulières de Perl dont un tutoriel est disponible ici : https://www.cs.tut.fi/~jkorpela/perl/regexp.html - l'annotation « meta-resource-method » montre comment rendre possible l'utilisation d'annotation uniquement dans les ressources et les méthodes ;
-
L'annotation « meta-data », en utilisant la valeur « TypeDeclaration » dans le nœud « allowedTarget », permet de déclarer qu'une annotation ne peut être utilisée que dans :
- les déclarations de type de données ;
- les déclarations de header ;
- les nœuds « queryParameters » ;
- les nœuds « baseUriParameters » ;
- une propriété placée dans n'importe lequel des nœuds cités ci-dessus et dans lequel la propriété « type » peut être utilisée.
La liste des valeurs pouvant être prises par le nœud « allowedTarget » est décrite ici : https://github.com/raml-org/raml-spec/blob/master/versions/raml-10/raml-10.md/#annotation-target-location
IV-B-12-c. Annotation de nÅ“ud scalaire▲
Il est également possible d'annoter les nœuds dits « scalaires ». Il s'agit des nœuds qui n'exigent qu'une valeur et n'ont pas de propriété supplémentaire (par exemple le nœud « baseUri »). La liste des nœuds scalaires est la suivante :
- displayName
- description
- type
- schema
- default
- example
- usage
- required
- content
- strict
- minLength
- maxLength
- uniqueItems
- minItems
- maxItems
- discriminator
- minProperties
- maxProperties
- discriminatorValue
- pattern
- format
- minimum
- maximum
- multipleOf
- requestTokenUri
- authorizationUri
- tokenCredentialsUri
- accessTokenUri
- title
- version
- baseUri
- mediaType
- extends
Afin d'annoter un nœud scalaire, il faut déjà savoir que cette notation est équivalente à celle-ci :
2.
baseUri
:
value
:
http
:
//www.example.com/api
Dès lors, nous pouvons annoter un nœud scalaire de la manière suivante :
2.
3.
baseUri
:
value
:
http
:
//www.example.com/api
(redirigeable):
true
IV-B-13. Le nÅ“ud « uses »▲
Il s'agit d'un nœud facultatif qui permet d'importer des bibliothèques externes. La création de telles bibliothèques sera expliquée dans le paragraphe VIII Création d'une bibliothèque RAML.
Exemple :
2.
uses
:
NewLibrary
:
NewLibrary.raml
Dans l'exemple ci-dessus, « NewLibrary.raml » est une bibliothèque externe qui contient des « traits » (voir le paragraphe IV.B.10 Le nœud « traits »), des « resourceTypes » (voir le paragraphe IV.B.11 Le nœud « resourceTypes ») et des « types » (voir le paragraphe IV.B.9 Le nœud « types »).
Le nœud « uses » permet donc à RAMLRESTful API Modeling Language de réutiliser des spécifications existantes.
V. Création de ressource et de ressource imbriquée▲
Nous avons étudié, dans la section précédente, la liste des nœuds, obligatoires et facultatifs, qui constituent la racine d'une spécification RAMLRESTful API Modeling Language. Nous avons volontairement omis de décrire les nœuds « ressources » même s'ils ont été évoqués dans les exemples qui précèdent (il s'agissait des nœuds « /véhicules », « /conducteurs » et « /conducteurs/{id} »).
En effet, les nœuds « ressources » méritent un paragraphe complet et nous nous appliquons à les étudier ci-dessous.
V-A. Formation de l'URI▲
Comme nous l'avons vu, les nœuds « ressources » sont identifiés par une URIUniform Resource Identifier relative qui commence par un « / ». Il s'agit soit d'un nœud qui se situe à la racine de la définition de l'APIApplication Programming Interface (par exemple : « /vehicules »), soit d'un enfant d'un nœud ressource (par exemple : « /conducteurs/{id} »). Dans ce dernier cas, on parle de « ressource imbriquée ».
Les ressources imbriquées peuvent elles-mêmes avoir d'autres ressources dites, elles aussi, imbriquées.
- Il est préférable que le nom des ressources soit au pluriel afin de permettre au développeur de deviner au premier coup d'œil qu'en manipulant ces ressources, il manipule des collections d'objets.
- Une collection de ressources sera donc adressée de cette manière :
« /v1/vehicules » qui permet, par exemple dans le cas de l'appel à la méthode « get », de déduire qu'on récupérera un ensemble de ressources de type « vehicule », ou, dans le cas de l'appel à la méthode « post », de déduire qu'on crée une instance dans la collection de véhicules.
L'instance d'une ressource sera, quant à elle, adressée de la manière suivante : « /v1/vehicules/2 » qui permet de déduire qu'on recherche le véhicule d'identifiant « 2 » dans la collection de véhicules. - Dans le cas où le nom d'une ressource est composé de plusieurs mots, il est préférable de séparer ces mots par un tiret (« - ») ou un underscore (« _ »).
Par exemple : « /v1/vehicules-legers » ou « /v1/vehicules_legers ». - En ce qui concerne le nommage du « body » de la requête, il semble que l'underscore (« _ ») ou la notation consistant à mettre la première lettre de l'attribut en minuscule et la première lettre de chaque autre mot en majuscule soient devenues conventionnelles.
Par exemple : « /vehicules?id_vehicule=2 » ou « /vehicules?idVehicule=2 »
Il en est de même pour les balises JSON et XML qui constituent le « body ».
Exemple :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
#%RAML 1.0
title
:
API de gestion d′un parc automobile
description
:
API permettant de gérer les opérations CRUD ( create, read, update, delete) relatives la gestion d′un parc de véhicules
version
:
v1
baseUri
:
https
:
//{
ville}
.garage.com/{
release}
baseUriParameters
:
ville
:
description
:
Sous-
domaine auquel l′API est accessible.
enum
:
[
paris, dijon]
release
:
description
:
Version de l′API
/vehicules
:
/conducteurs
:
/{
conducteurId}:
#On a affaire à une ressource imbriquée
uriParameters
:
conducteurId
:
type
:
integer
/permis
:
/{
idPermis}:
uriParameters
:
type
:
integer
Les URIUniform Resource Identifier absolues pour les ressources ci-dessus sont les suivantes (selon l'ordre de leur déclaration) :
- https://{ville}.garage.com/{release}/vehicules
- https://{ville}.garage.com/{release}/conducteurs
- https://{ville}.garage.com/{release}/conducteurs/{conducteurId}
- https://{ville}.garage.com/{release}/conducteurs/{conducteurId}/permis
- https://{ville}.garage.com/{release}/conducteurs/{conducteurId}/permis/{idPermis}
Les URIUniform Resource Identifier ne doivent pas être identiques à une autre. Les paramètres ne sont toutefois pas évalués par le moteur RAMLRESTful API Modeling Language. En conséquence, les URIUniform Resource Identifier ci-dessous sont permises :
2.
3.
/conducteurs/{
conducteurId}:
/conducteurs/{
conducteurName}:
/conducteurs/moi
:
Lors de la spécification de services web RESTRepresentational State Transfer, il est nécessaire de penser aux performances du serveur lors d'un très grand nombre d'appels.
Ainsi, même si on peut naturellement penser créer une ressource par URL, il est important de garder à l'esprit que ceci peut surcharger inutilement le serveur.
Par exemple, si on a trois ressources (« véhicules », « conducteurs », « permis » et qu'on souhaite récupérer le permis de conduire d'un conducteur qui conduit un véhicule donné, il est nécessaire d'effectuer trois appels :
- https://dijon.garage.com/v1/vehicules/2
puis
- https://dijon.garage.com/v1/conducteurs/3
puis
- https://dijon.garage.com/v1/permis/5
Toutes ces informations sont pourtant souvent utilisées ensemble. Nous générons donc trois appels alors qu'un seul peut suffire.
En revanche, il ne faut pas non plus que les réponses contiennent trop de données inutiles, car les échanges seraient surchargés inutilement.
Il est donc nécessaire de regrouper, dans une même URL, les ressources ayant des dépendances entre elles et auxquelles l'utilisation de l'une entraînera l'utilisation des autres quasi systématiquement.
Pour reprendre l'exemple que nous venons d'évoquer, une URL de la forme « /v1/vehicules/2/conducteurs/permis » contenant deux niveaux d'imbrication au maximum permet de limiter le volume de données retourné tout en récupérant les données susceptibles de nous intéresser le plus.
Au-delà de deux niveaux d'imbrication, le flux d'informations retournées risquerait de contenir des informations obsolètes qui ne subiront aucun traitement et qui risqueraient de surcharger le flux inutilement.
V-B. Les propriétés des ressources▲
Nous avons entrevu, dans la section précédente, la propriété « baseUriParameters » qui permet de fournir des détails concernant le paramètre de l'URIUniform Resource Identifier comme son type par exemple.
Il existe d'autres propriétés que nous avons déjà utilisées précédemment pour définir une ressource telles :
- displayName (optionnelle) qui permet d'afficher le nom de la ressource ;
- description (optionnelle) qui permet de décrire précisément l'utilité de la ressource ;
- is (optionnelle) qui permet de faire appel à un/des « traits » ;
- type (optionnelle) qui permet de déclarer le type de la ressource ou de la ressource imbriquée ;
-
les méthodes HTTP de RESTRepresentational State Transfer (« get », « post », « delete » et « put » qui permettent respectivement de récupérer, insérer, supprimer et mettre à jour).
Il existe également d'autres méthodes HTTP pour RESTRepresentational State Transfer qui sont les suivantes :-
patch
Le corps d'une requête « PATCH » contient uniquement les instructions permettant de mettre à jour une instance de classe (contrairement au corps d'une requête « PUT » qui contient une représentation complète de la ressource à modifier). Le corps de la requête « PATCH » est donc d'un type différent de la représentation de la ressource à modifier. Par conséquent, contrairement à la requête « PUT », la requête « PATCH » ne peut pas créer de nouvelle ressource ;
Exemple : la modification de l'adresse mail de l'utilisateur « 123 » en appliquant la méthode « PATCH » sur sa représentation JSON ressemble à ceci :Exemple PATCHSélectionnezPATCH
/
users/
123
[
{
"op"
:
"replace"
,
"path"
:
"/email"
,
"value"
:
"new.email@example.org"
}
]
-
-
Options
Cette méthode permet de récupérer les opérations qui peuvent être appelées sur une ressource particulière.
Par exemple, si une ressource est en lecture seule, alors envoyer une requête « OPTIONS » sur cette ressource retourne un header de la réponse nommé « Allow » avec les valeurs « GET » et « HEAD ». « GET » et « HEAD » sont donc les deux seules opérations qu'on peut appliquer sur la ressource ;
Exemple :Exemple OPTIONSélectionnez1.
2.
3.
4.
5.OPTIONS www.garage.com/aboutme Response Headers Allow : HEAD, GET
- Head
Cette méthode permet de récupérer des informations contenues dans le « header » de la réponse. C'est une méthode similaire à la méthode « get » sauf qu'elle retourne des informations concernant les métadonnées d'une ressource. La méthode « head » peut donc être utilisée pour savoir si une ressource existe sur le serveur ou pour récupérer des informations sur cette ressource.
V-C. Les propriétés des méthodes▲
Comme nous l'avons vu dans la section précédente, les méthodes pouvant être utilisées dans les ressources sont les méthodes « get », « post », « put », « delete », « patch », « options » et « head ».
Ces méthodes possèdent des propriétés que nous avons déjà énumérées dans les exemples ci-dessus telles :
- displayName (optionnelle) qui permet d'afficher le nom de la méthode ;
- description qui permet de décrire précisément l'utilité de la méthode ;
- queryParameters (optionnelle) qui permet de spécifier les filtres à fournir à une méthode pour effectuer une requête (Voir le paragraphe IV.B.10 Le nœud « traits ») ;
-
responses (optionnelle) qui permet de décrire les réponses HTTP attendues lors de l'appel à la méthode.
En fonction de la valeur de retour, on peut spécifier qu'on attend une structure spécifique dans le « body » et dans le « header » (voir l'explication du nœud « headers » ci-dessous) ;
Par exemple :Exemple responseSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25./
vehicules
:
get
:
responses
:
200
:
body
:
type
:
Vehicule[]
post
:
body
:
type
:
Vehiculeresponses
:
201
:
headers
:
Location
:
example
:
/vehicules/45612
body
:
application/json
:
type
:
!include schemas/vehicule.json
text/xml
:
type
:
!include schemas/vehicule.xsd
422
:
body
:
properties
:
error
:
example
:
error
:
Impossible de traiter les instructionsÂ:
données erronées -
body (optionnelle) qui permet de décrire le contenu du body de la requête. Il peut s'agir, dans le cas d'une méthode « put » ou d'une méthode « post » de préciser, dans le « body », la structure de la ressource à créer ou à modifier tout en précisant le format dans lequel les données seront transmises (par exemple : « application/json ») ;
-
protocols (optionnelle) qui permet de spécifier le protocole utilisé par la méthode (« HTTP » ou « HTTPS »). La valeur de cette propriété n'est pas sensible à la casse ;
-
is (optionnelle) qui permet, à la méthode, de faire appel à un/des « traits » ;
- (<annotationName
>
) (optionnelle) qui permet d'utiliser une annotation (dont le nom est toujours entre parenthèses).
À cette liste, nous pouvons ajouter les propriétés :
-
headers qui permet de spécifier la structure du « header » de la requête envoyée par la méthode. La valeur du nœud « headers » est composée d'un ensemble de déclarations de propriétés. Chaque nom de propriété définit un nom de « header » autorisé. Par défaut, la valeur de la propriété est de type « string ».
Si une déclaration de « header » spécifie un type « array » pour la valeur du « header », alors plusieurs instances de ce « header » devront être autorisées par le processeur dans la requête ou la réponse. En revanche, si un autre type que « array » est spécifié alors le processeur ne doit pas autoriser plus d'une instance de ce « header » dans la requête ou la réponse.
Exemple (repris des spécifications RAMLRESTful API Modeling Language) :Exemple headerSélectionnez1.
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.#%RAML 1.0
title
:
Example with headerstraits
:
chargeable
:
headers
:
X-
Dept
:
type
:
arraydescription
:
|
Un département peut être chargé. Plusieurs de ces headers sont autorisés.items
:
pattern
:
^\d+
\-
\w+
$example
:
230
-
OCTOtraceable
:
headers
:
X-
Tracker
:
description
:
Code pour tracer les appels à l′API de bout en boutpattern
:
^\w{
16
}
$example
:
abcdefghijklmnop /users
:
get
:
is
:
[
chargeable, traceable]
description
:
|
Les interactions HTTP ressembleront à ceciÂ:
GET /users HTTP/1
.1
X-
Dept
:
18
-
FINANCE X-
Dept
:
200
-
MISC X-
Tracker
:
gfr456d03ygh38s2headers
:
X-
Dept
:
example
:
[
18
-
FINANCE,200
-
MISC]
X-
Tracker
:
example
:
gfr456d03ygh38s2 - securedBy qui permet de spécifier les schémas de sécurité. Nous traiterons de la sécurité dans le paragraphe VII. La gestion de la sécurité.
VI. Les types▲
VI-A. Les types par défaut▲
VI-A-1. Le type « object »▲
Il est possible de déclarer un type de donnée de la manière suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
types
:
Conducteur
:
properties
:
nom
:
type
:
string
minLength
:
2
maxLength
:
50
required
:
true
prenom
:
type
:
string
minLength
:
2
maxLength
:
50
required
:
true
Dans cet exemple, on ne sait pas de quel type est le type « Conducteur ». Néanmoins, du fait qu'il contient des propriétés (introduites par le mot clé « properties »), sachez qu'il est de type « object » par défaut et que la déclaration ci-dessus est équivalente à celle-ci :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
types
:
Conducteur
:
type
:
object
properties
:
nom
:
type
:
string
minLength
:
2
maxLength
:
50
required
:
true
prenom
:
type
:
string
minLength
:
2
maxLength
:
50
required
:
true
Dans l'exemple ci-dessus, on a ajouté la ligne : type: object.
VI-A-2. Le type « string »▲
Si la déclaration d'un type ne contient pas de « properties » ou de « type », alors le type par défaut est le type « string ».
Par exemple, la propriété « name » ci-dessous ne contient pas de nœud « properties », ni même de nœud « type » donc « name » est de type « string » :
2.
3.
4.
types
:
Person
:
properties
:
name
:
#Possibilité de ne pas renseigner le type
VI-A-3. Le type « any »▲
Le type « any » est appliqué aux nœuds « body » qui ne contiennent pas de nœud « properties » ou « type ».
Exemple :
2.
3.
body
:
application/json
:
# Le type par défaut est `any`
Si un type de média par défaut (voir le paragraphe IV.B.7 Le nœud « mediaType ») n'a pas été défini, alors la définition du body peut se résumer à ceci :
2.
body
:
# Le type par défaut est `any`
Dans ce dernier cas, le body peut contenir n'importe quelle structure.
VI-B. Les types scalaires▲
Il s'agit des types natifs de RAMLRESTful API Modeling Language. Nous les décrirons dans les sections ci-dessous.
VI-B-1. Le type « string »▲
Il s'agit du type « chaîne de caractères ». Les propriétés qui lui sont attachées sont les suivantes :
- pattern
Il s'agit d'un nœud facultatif qui reçoit, pour valeur, une expression régulière. Les expressions régulières sont basées sur les expressions régulières de Perl dont un tutoriel est disponible ici : https://www.cs.tut.fi/~jkorpela/perl/regexp.html ; - minLength
Il s'agit d'un nœud facultatif, dont la valeur par défaut est 0, et qui a pour valeur la longueur minimum de la chaîne de caractères ; - MaxLength
Il s'agit d'un nœud facultatif, dont la valeur par défaut est 2147483647, et qui a pour valeur la longueur maximum de la chaîne de caractères.
Exemple :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
types
:
Conducteur
:
properties
:
nom
:
type
:
string
minLength
:
2
maxLength
:
50
required
:
true
prenom
:
type
:
string
minLength
:
2
maxLength
:
50
required
:
true
VI-B-2. Le type « number »▲
Il s'agit d'un type qui regroupe tous les types numériques de JSON y compris le type « integer ».
Les propriétés du type « number » sont les suivantes :
- minimum
Il s'agit d'un nœud facultatif qui précise la valeur minimum que peut prendre la variable ; - maximum
Il s'agit d'un nœud facultatif qui précise la valeur maximum que peut prendre la variable ; -
format
Il s'agit d'un nœud facultatif qui fournit le format de la valeur. Ce nœud prend l'une des valeurs suivantes :- int32 ;
- int64Â ;
- int ;
- long ;
- float ;
- double.
- multipleOf
Ce nœud facultatif permet de préciser que la valeur qui sera prise par l'instance devra obligatoirement être un multiple de la valeur renseignée pour la propriété multipleOf. Le résultat de la division de la valeur de l'instance par la valeur de la propriété multipleOf doit être un entier.
Exemple :
2.
3.
4.
5.
6.
types
:
ChevalFiscal
:
type
:
number
minimum
:
2
maximum
:
9
format
:
int
VI-B-3. Le type « integer »▲
Il s'agit d'un sous-ensemble du type « number » qui peut être positif ou négatif. Il hérite des propriétés du type « number ».
Exemple :
2.
3.
4.
5.
6.
7.
types
:
Annee
:
type
:
integer
minimum
:
1950
maximum
:
2016
format
:
int8
multipleOf
:
1
VI-B-4. Le type « boolean »▲
Il s'agit du type booléen comme nous le trouvons dans de nombreux langages qui prend, bien sûr, pour seules valeurs « true » ou « false ».
Exemple :
2.
3.
types
:
ToitOuvrant
:
type
:
boolean
VI-B-5. Le type « date »▲
Le type « date » se décline en plusieurs types selon les informations plus ou moins détaillées qu'on souhaite obtenir :
-
date-only
Permet de stocker une date au format aaaa-mm-jj ;Exemple type "date-only"Sélectionnez1.
2.
3.
4.date_naissance
:
required
:
true
type
:
date-
onlyexample
:
1981
-
05
-
04
-
time-only
Permet de stocker une heure au format hh:mm:ss ;Exemple type "time-only"Sélectionnez1.
2.
3.heure_departÂ
:
type
:
time-
onlyexample
:
14
:
00
:
26
-
datetime-only
Il s'agit d'un mélange entre le format « date-only » et le format « time-only ». Ce format est stocké sous la forme aaaa-mm-jjThh:mm:ss. Le « T » sépare la date de l'heure ;Example type "datetime-only"Sélectionnez1.
2.
3.heure_depart
:
type
:
datetime-
onlyexample
:
2016
-
05
-
15T14
:
00
:
26
- datetime
Concernant ce type, le format de la date dépendra de la valeur du nœud « format ». Si le nœud « format » est omis ou si sa valeur est « rfc3339 », le format RFC3339 est appliqué.
Si le nœud « format » est spécifié alors le format défini par la norme RFC2616 est utilisé.
2.
3.
4.
5.
6.
7.
8.
heure_depart
:
type
:
datetime
example
:
2016
-
02
-
28T16
:
41
:
41
.090Z
format
:
rfc3339 # Par défaut donc pas besoin de spécifier
heure_arrivee
:
type
:
datetime
example
:
Sun, 28
Feb 2016
16
:
41
:
41
GMT
format
:
rfc2616 # Requis sinon, l'exemple n'est pas valide
VI-B-6. Le type « file »▲
Ce type permet de contraindre le contenu des informations envoyées depuis un formulaire.
Lorsque ce type est utilisé dans le contexte d'un formulaire web, il devrait être représenté par un fichier « uploadé » valide au format JSON. Le contenu de ce fichier devrait être une chaîne de caractères encodée en base 64.
Le type « file » contient les propriétés suivantes :
- fileTypes
Il s'agit d'une propriété facultative qui prend pour valeur une liste de « content type » (type de contenu) valides pour le fichier. Le type de fichier*
/*
doit être une valeur valide. Une liste de « content type » est fournie à cette adresse : http://www.freeformatter.com/mime-types-list.html ; - minLength
Il s'agit d'un nœud facultatif qui, par défaut, prend la valeur 0, et qui permet de spécifier la taille minimum en octets du fichier transmis. Cette valeur doit être égale ou supérieure à 0. - maxLength
Il s'agit d'un nœud facultatif qui, par défaut, prend la valeur 2147483647, et qui permet de spécifier la taille maximum en octets du fichier transmis. Cette valeur doit être égale ou supérieure à 0.
VI-B-7. Le type « array »▲
Il s'agit du type « tableau ». Il existe deux manières équivalentes de définir un tableau :
-
par utilisation des crochets « [] » à la fin du type ;
Exemple :Déclaration d'un type tableau avec crochetSélectionnez1.
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.types
:
Vehicule
:
properties
:
marque
:
type
:
stringrequired
:
true
(meta-
data):
Sur la déclaration de la"properties"
modele
:
type
:
stringrequired
:
true
categorie
:
type
:
stringrequired
:
true
enum
:
-
Citadine-
Berline-
Monospace-
SUVplaces
:
integer#Par défaut required: true.
prix
:
required
:
false
type
:
numbercouleur
:
required
:
true
enum
:
-
Blanc-
Noir-
Gris-
Rouge-
Jaune-
BleuVehicules
:
type
:
Vehicule[]
minItems
:
1
uniqueItems
:
false
example
:
# example that contains array
-
# item 1
marque
:
RENAULTmodele
:
Sceniccategorie
:
Monospaceplaces
:
7
prix
:
31500
couleur
:
Gris-
# item 2
marque
:
RENAULTmodele
:
Meganecategorie
:
Berlineplaces
:
5
prix
:
19500
couleur
:
RougeDans l'exemple ci-dessus, on voit que le type « Vehicules » est un tableau d'instances du type « vehicule ».
- par utilisation du mot array en tant que valeur du nœud « type ».
Exemple :
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.
types
:
Vehicule
:
properties
:
marque
:
type
:
string
required
:
true
(meta-
data):
Sur la déclaration de la "properties"
modele
:
type
:
string
required
:
true
categorie
:
type
:
string
required
:
true
enum
:
-
Citadine
-
Berline
-
Monospace
-
SUV
places
:
integer #Par défaut required: true.
prix
:
required
:
false
type
:
number
couleur
:
required
:
true
enum
:
-
Blanc
-
Noir
-
Gris
-
Rouge
-
Jaune
-
Bleu
Vehicules
:
type
:
array
items
:
Vehicule
minItems
:
1
uniqueItems
:
false
example
:
# exemple qui contient un tableau
-
# item 1
marque
:
RENAULT
modele
:
Scenic
categorie
:
Monospace
places
:
7
prix
:
31500
couleur
:
Gris
-
# item 2
marque
:
RENAULT
modele
:
Megane
categorie
:
Berline
places
:
5
prix
:
19500
couleur
:
Rouge
Comme on peut le voir dans l'exemple ci-dessus, les propriétés des tableaux sont les suivantes :
- uniqueItems
Il s'agit d'un nœud facultatif qui indique, si sa valeur est à « true », que l'objet dans le tableau doit être unique ; - items
Il s'agit d'un nœud facultatif qui indique de quel type sont tous les objets qui sont stockés dans le tableau ; - minItems
Il s'agit d'un nœud facultatif qui indique le nombre minimum d'objets qui sont dans le tableau ; - maxItems
Il s'agit d'un nœud facultatif qui indique le nombre maximum d'objets qui sont dans le tableau.
VI-B-8. Le type « Nil »▲
C'est un type qui permet de dire qu'aucune valeur n'est autorisée pour le champ déclaré de ce type.
Le type « nil » utilisé dans des headers, des paramètres d'URIUniform Resource Identifier, ou des paramètres de requête permet uniquement d'utiliser la chaîne de caractères « nil » (sensible à la casse). Inversement, une instance ayant pour valeur la chaîne de caractères « nil » (sensible à la casse), lorsqu'elle est de type « nil », voit sa valeur mise à néant.
Exemples (repris des spécifications RAMLRESTful API Modeling Language ) :
2.
3.
4.
5.
6.
7.
8.
9.
types
:
NilValue
:
type
:
object
properties
:
name
:
comment
:
example
:
name
:
Fred
comment
:
#Ne pas fournir de valeur ici n'est pas permis
2.
3.
4.
5.
6.
7.
8.
9.
types
:
NilValue
:
type
:
object
properties
:
name
:
comment
:
nil
example
:
name
:
Fred
comment
:
#Fournir une valeur ici n'est pas permis
2.
3.
4.
5.
6.
7.
8.
9.
types
:
NilValue
:
type
:
object
properties
:
name
:
comment
:
nil |
string # equivalent à « comment: string? »
example
:
name
:
Fred
comment
:
# Fournir, ou pas, une valeur ici est permise
VI-B-9. Le type « union »▲
Dans le dernier exemple de la section précédente, le caractère « | » est un caractère d'union en RAMLRESTful API Modeling Language. Il est utilisé pour permettre à une instance de donnée de permettre l'affectation de valeurs de type différent. Dans l'exemple précédent, la propriété « comment » peut ne pas avoir de valeur ou peut prendre une valeur sous forme de chaîne de caractères.
L'union de type peut être utilisée de manière plus complexe comme le présente l'exemple ci-dessous (repris des spécifications RAMLRESTful API Modeling Language) :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
types
:
HasHome
:
type
:
object
properties
:
homeAddress
:
string
Cat
:
type
:
object
properties
:
name
:
string
color
:
string
Dog
:
type
:
object
properties
:
name
:
string
fangs
:
string
HomeAnimal
:
[
HasHome , Dog |
Cat ]
Dans l'exemple ci-dessus, une instance de type « HomeAnimal » hérite des caractéristiques du type « HasHome » et des caractéristiques d'un des types « Dog » ou « Cat ».
De manière encore plus complexe, il est possible d'écrire une déclaration de ce type :
2.
types
:
HomeAnimal
:
[
HasHome |
IsOnFarm , Dog |
Cat |
Parrot ]
Cette déclaration spécifie que les instances du type « HomeAnimal » peuvent hériter des types :
- [HasHome, Dog]Â ;
- [HasHome, Cat]Â ;
- [HasHome, Parrot]Â ;
- [IsOnFarm, Dog]Â ;
- [IsOnFarm, Cat]Â ;
- [IsOnFarm, Parrot].
À travers le type « union », c'est l'héritage multiple qui apparaît.
VI-B-10. Utilisation des schémas XML et des schémas JSON▲
Il est possible, avec RAMLRESTful API Modeling Language, d'utiliser des schémas XML et des schémas JSON pour décrire un type ou le corps d'une requête ou d'une réponse en intégrant les schémas via la propriété type. L'appel de ces schémas, situés dans des fichiers externes, s'effectue par utilisation du mot « !include »
Exemples :
2.
types
:
Person
:
!include voiture.json
2.
3.
4.
5.
6.
7.
/voitures
:
get
:
responses
:
200
:
body
:
application/json
:
type
:
!include voiture.json
Sur les types définis par un schéma JSON ou XML, il n'est pas possible d'ajouter de propriétés supplémentaires. Par exemple, cette définition est invalide :
2.
3.
4.
5.
types
:
Voiture
:
type
:
!include voiture.json
properties
:
# invalide
electrique
:
boolean
En revanche, on peut ajouter des annotations (y compris « displayName » et « description ») et des exemples :
2.
3.
4.
types
:
Voiture
:
type
:
!include voiture.json
description
:
Il s′agit d′un schéma décrivant une voiture
Il n'est pas non plus possible d'utiliser les types définis à partir de schéma JSON ou XML dans les propriétés des types.
Contre-exemple :
2.
3.
4.
5.
6.
7.
types
:
Voiture
:
type
:
!include voiture.json
description
:
Il s′agit d′un schéma décrivant une voiture
Parking
:
properties
:
parc
:
Voiture[]
# Utilisation invalide à cause des crochets « [] » et de l'utilisation du type Voiture dans les propriétés d'un type.
VI-C. La gestion du polymorphisme▲
Lorsque le corps d'un message contient des instances dont le type est ambigu à cause du type « union » ou de l'héritage, il est compliqué de différencier le type des instances au cours de l'exécution et ainsi d'affecter l'instance à une variable de type approprié.
Le processeur RAMLRESTful API Modeling Language peut sélectionner automatiquement un type spécifique parmi un ensemble de types possibles. Pour cela, il est nécessaire d'utiliser un attribut, nommé « discriminator » qui permet, par la valeur qu'il stocke, d'identifier le type de l'objet auquel cet attribut appartient.
L'attribut « discriminator », qui appartient au type parent, permet de renseigner le nom de la propriété, héritée du type parent, qui permet de faire la différence entre les différents types enfants.
C'est l'attribut « discriminatorValue » qui permet de stocker la valeur que prendra la propriété sur laquelle s'applique l'attribut « discriminator ». Par défaut, la valeur de « discriminatorValue » est le nom du type enfant.
Exemple :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
types
:
Vehicule
:
type
:
object
discriminator
:
categorie # se réfère à la propriété "categorie" de l'objet "Vehicule"
properties
:
categorie
:
string # contient le nom du genre de l'instance `Vehicule`
nom
:
string
Poids_Lourd
:
# categorie peut être égale à "Poids_Lourd". Il s'agit de la valeur par défaut pour l'attribut "discriminatorValue"
type
:
Vehicule
properties
:
plId
:
integer
Vehicule_Leger
:
# categorie peut être égale à "Vehicule_Leger". Il s'agit de la valeur par défaut pour l'attribut "discriminatorValue"
type
:
Vehicule
properties
:
vlId
:
integer
Les données liées à l'exemple ci-dessus peuvent être similaires à celles-ci :
2.
3.
4.
5.
6.
7.
data
:
-
nom
:
Scenic
vlId
:
1
categorie
:
Vehicule_Leger
-
nom
:
Actros
plId
:
2
categorie
:
Poids_Lourd
Dans l'exemple ci-dessus, on peut voir que, sans préciser la valeur de l'attribut « discriminatorValue », c'est le nom du type enfant qui est utilisé pour renseigner la propriété « categorie ».
Vous pouvez également décider de modifier la valeur par défaut de l'attribut « discriminatorValue ». On précise alors cette valeur dans chacun des types enfant comme le présente l'exemple ci-dessous :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
types
:
Vehicule
:
type
:
object
discriminator
:
categorie # se réfère à la propriété "categorie" de l'objet "Vehicule"
properties
:
categorie
:
string
nom
:
string
Poids_Lourd
:
# categorie peut être égale à "Poids_lourds". Il s'agit de la valeur par défaut pour l'attribut "discriminatorValue"
type
:
Vehicule
discriminatorValue
:
PL
properties
:
plId
:
integer
Vehicule_Leger
:
# categorie peut être égale à "Poids_lourds". Il s'agit de la valeur par défaut pour l'attribut "discriminatorValue"
type
:
Vehicule
discriminatorValue
:
VL
properties
:
vlId
:
integer
Les données liées à l'exemple ci-dessus peuvent être similaires à celles-ci :
2.
3.
4.
5.
6.
7.
data
:
-
nom
:
Scenic
vlId
:
1
categorie
:
VL
-
nom
:
Actros
plId
:
2
categorie
:
PL
Les attributs « discriminator » et « discriminatorValue » sont valables uniquement dans le nœud « types ». Il est interdit de les utiliser dans les déclarations de type en ligne ou dans les types « union ».
Exemple :
2.
3.
4.
5.
# invalide dans une déclaration de type en ligne
application/json
:
discriminator
:
categorie
properties
:
categorie
:
string
2.
3.
4.
# Invalide pour le type « union »
VehiculeLegerOuLourd
:
type
:
Vehicule_Leger |
Poids_Lourd
discriminator
:
aUneBenne
VII. La gestion de la sécurité▲
En plus des nœuds de base que nous avons étudiés plus haut, RAMLRESTful API Modeling Language permet d'inclure des mécanismes de sécurisation d'accès aux données, d'identification de requêtes, de détermination de niveau d'accès et de visibilité de données.
VII-A. Les schémas de sécurité supportés par RAML▲
Pour gérer les mécanismes de sécurisation, RAMLRESTful API Modeling Language supporte les types de schémas de sécurité ci-dessous :
-
OAuth 1.0 et OAuth 2.0
La raison d'être du protocole OAuth est de s'identifier à partir du profil d'une autre application comme le proposent certains sites en nous permettant de nous identifier à partir de notre compte Twitter ou Facebook.
L'intérêt majeur d'OAuth vient du fait que l'utilisateur n'a plus besoin de fournir ses informations d'identification à une application tierce. En contrepartie, le site sur lequel on se connecte peut tout faire avec le compte facebook ou twitter. Pour pallier cela, Oauth met en place un mécanisme de délégation d'autorisation entre les différentes entités.
Cette méthode d'authentification, basée, pour plus de sécurité, sur l'utilisation de HTTPS, utilise un accès par jeton.
Il s'agit d'un identifiant utilisé par un client pour accéder à une ressource. Tant que le jeton est valide, le client peut accéder à la ressource. Le jeton peut être révoqué par le serveur d'autorisation à partir du moment où il est expiré ou à partir du moment où le client a choisi de le révoquer.
Le scénario général est le suivant :- Le client demande au détenteur de la ressource (par exemple facebook ou twitter), l'autorisation de demander un jeton.
- En cas d'autorisation, le détenteur de la ressource envoie son consentement au serveur d'autorisation qui finit par envoyer le jeton au client. Le serveur d'autorisation aura préalablement déterminé la durée de vie de ce jeton.
-
Le jeton d'accès sera ensuite envoyé par le client, dans le header ou en paramètres, à chacune de ses requêtes vers le serveur de ressources.
Pour plus d'informations sur le protocole OAuth, voir les sites : http://blog.netapsys.fr/oauth-comment-ca-marche/ et http://www.bubblecode.net/fr/2016/01/22/comprendre-oauth2/.
Ces deux sites présentent plusieurs types d'autorisations possibles au sein du protocole OAuth.
-
Les protocoles OAuth 1.0 et OAuth 2.0 sont généralement utilisés pour sécuriser les APIApplication Programming Interface RESTRepresentational State Transfer.
Les caractéristiques majeures qu'apporte OAuth 2.0 par rapport à OAuth 1.0 sont les suivantes :- Gestion de l'authentification et de l'habilitation des ressources par tout type d'application (native mobile, native tablette, application JavaScript, application web de type serveur, application batch/back-office) avec ou sans consentement du propriétaire des ressources.
-
OAuth 2.0 est devenu le standard de sécurisation des APIApplication Programming Interface RESTRepresentational State Transfer.
-
Basic Authentication
Lors de l'envoi d'une requête au serveur, le client s'identifie à l'aide d'un nom d'utilisateur et d'un mot de passe. Le serveur répond ensuite en envoyant les données requises ou en envoyant un message d'erreur. Les données ne sont pas cryptées et sont seulement encodées en base 64.
Pour être sécurisée, l'authentification basique doit être utilisée sur une couche « transport » implémentant le protocole SSL (Secure Sockets Layer). -
Digest Authentication
Cette méthode d'authentification est plus sécurisée que l'authentification basique, car elle ne requiert pas le protocole SSL, ce qui rend les échanges plus rapides. Les identifiants de connexion sont envoyés encryptés au serveur.- Le client envoie une requête au serveur.
-
Le serveur y répond en envoyant :
- un code appelé « nonce » généré par le serveur à chaque réponse 401 (demande d'authentification) ;
- une chaîne de caractères appelée « realm » qui contient au moins le nom de la machine et le nom du groupe d'utilisateur requis afin que l'utilisateur sache avec quel identifiant et quel mot de passe il doit s'identifier.
- Le client applique alors une fonction de hashage sur ce « nonce », mais également sur l'identifiant et le mot de passe encryptés ainsi que sur le « realm » et renvoie l'ensemble au serveur.
-
Le serveur répond avec l'information demandée par le client si la valeur de hashage retournée par le client correspond à la valeur calculée par le serveur.
Avec ce schéma de sécurité, plusieurs options de sécurité sont optionnelles. Si le paramètre « QOP » (Quality of Protection) n'est pas renseigné par le serveur, le client échangera dans un mode de sécurité réduit.
De plus, cette authentification est vulnérable si un pirate informatique arrive à se placer au milieu des conversations entre le client et le serveur et demande au client d'appliquer le mode de sécurité réduit. L'authentification « digest » ne fournit aux clients aucun mécanisme permettant la vérification de l'identité du serveur.
-
Pass through
L'authentification « Pass through » se déroule de la manière suivante :- Le client envoie une requête « HTTP », « HTTPS », « FTP » ou « Telnet » au serveur.
- Le serveur intercepte la demande et voit que la demande envoyée nécessite une authentification depuis une base de données locale ou depuis un autre serveur d'authentification.
- Le serveur fait afficher une demande d'identifiant et de mot de passe sur l'écran du client.
- L'utilisateur répond en envoyant son nom d'utilisateur et son mot de passe.
- Soit le serveur vérifie la cohérence des informations retournées dans sa base de données, soit il envoie ces informations de connexion au serveur d'authentification externe.
-
Si les informations retournées par l'utilisateur sont cohérentes, le serveur informe le client que son identification est correcte.
- x-{other}
L'authentification de l'APIApplication Programming Interface est reliée à une autre méthode d'authentification.
VII-B. Le nÅ“ud « securitySchemes »▲
Les mécanismes de sécurité énumérés dans la section précédente sont spécifiés par l'intermédiaire du nœud securitySchemes.
Voyons d'abord un exemple de définition de schéma de sécurité extrait des spécifications de RAMLRESTful API Modeling Language (https://github.com/raml-org/raml-spec/blob/master/versions/raml-10/raml-10.md/#security-schemes ) :
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.
#%RAML 1.0
title
:
Dropbox API
version
:
1
baseUri
:
https
:
//api.dropbox.com/{
version}
securitySchemes
:
oauth_2_0
:
description
:
|
Dropbox supporte OAuth 2
.0
pour authentifier toutes les requêtes de l′API
type
:
OAuth 2
.0
describedBy
:
headers
:
Authorization
:
description
:
|
Utilisé pour envoyer un jeton d′accès OAuth 2
.0
valide. À ne pas utiliser avec le paramètre "access_token"
type
:
string
queryParameters
:
access_token
:
description
:
|
Utilisé pour envoyer un jeton d′accès OAuth 2
.0
valide. À ne pas utiliser avec le header "Authorization"
.
type
:
string
responses
:
401
:
description
:
|
Jeton corrompu ou expiré. Ceci survient si l′utilisateur ou dropbox révoque ou expire un jeton d′accès. Pour solutionner ce problème, ré-
authentifier l′utilisateur.
403
:
description
:
|
Mauvaise requête OAuth (mauvaise clé, mauvais nonce ou temps écoulé). La réauthentification de l′utilisateur n′aidera en rien.
settings
:
authorizationUri
:
https
:
//www.dropbox.com/1
/oauth2/authorize
accessTokenUri
:
https
:
//api.dropbox.com/1
/oauth2/token
authorizationGrants
:
[
authorization_code, implicit ]
oauth_1_0
:
description
:
|
OAuth 1
.0
continue d′être supporté par toutes les requêtes de l′API mais OAuth 2
.0
est préféré.
type
:
OAuth 1
.0
settings
:
requestTokenUri
:
https
:
//api.dropbox.com/1
/oauth/request_token
authorizationUri
:
https
:
//www.dropbox.com/1
/oauth/authorize
tokenCredentialsUri
:
https
:
//api.dropbox.com/1
/oauth/access_token
Nous voyons que le nœud « securitySchemes » comporte plusieurs nœuds fils qui sont les suivants :
- Le nœud « type » ;
- Le nœud « displayName » ;
- Le nœud « description » ;
- Le nœud « describedBy » ;
- Le nœud settings.
Nous expliquerons, dans les sections ci-dessous, le rôle de chacun de ces nœuds.
VII-B-1. Le nÅ“ud « type »▲
Le nœud « type » est obligatoire et permet de spécifier quel(s) type(s) de schéma de sécurité peut/peuvent être utilisé(s) par l'APIApplication Programming Interface.
Les valeurs que peut prendre ce nœud sont :
- OAuth 1.0Â ;
- Oauth 2.0Â ;
- Basic Authentication ;
- Digest Authentication ;
- Pass Through ;
- x-<other>.
VII-B-2. Le nÅ“ud « displayName »▲
Le nœud « displayName » est facultatif. Il n'est d'ailleurs pas utilisé dans l'exemple ci-dessus. Il permet de nommer le schéma de sécurité en langage humain.
VII-B-3. Le nÅ“ud « description »▲
Le nœud « description » est facultatif et permet de décrire le schéma de sécurité choisi. La valeur prise par ce nœud peut être formatée en langage markdown.
VII-B-4. Le nÅ“ud « describedBy »▲
Le nœud « describedBy » est facultatif. Il permet de décrire les composants des requêtes (headers, paramètres, réponses) déterminés par le schéma de sécurité. Il est de bon aloi, même pour les schémas de sécurité standards, de renseigner chacune des propriétés de ce nœud. Les propriétés du nœud « describedBy » sont les suivantes :
VII-B-4-a. Le nÅ“ud « headers »▲
Le nœud « headers » est optionnel.
Il permet de spécifier la structure du « header » de la requête envoyée par la méthode. La valeur du nœud « headers » est composée d'un ensemble de déclarations de propriétés. Chaque nom de propriété définit un nom de « header » autorisé. Par défaut, la valeur de la propriété est de type « string ».
Si une déclaration de « header » spécifie un type « array » pour la valeur du header, alors plusieurs instances de ce « header » devront être autorisées par le processeur dans la requête ou la réponse. En revanche, si un autre type que « array » est spécifié, alors le processeur ne doit pas autoriser plus d'une instance de ce « header » dans la requête ou la réponse.
VII-B-4-b. Le nÅ“ud « queryParameters »▲
Le nœud « queryParameters » est optionnel. Il permet de spécifier les paramètres utilisés par le schéma pour autoriser la requête.
VII-B-4-c. Le nÅ“ud « responses »▲
Le nœud « responses » est optionnel. Il permet de décrire les réponses HTTP attendues lors de l'appel à la méthode. Ce nœud est composé de nœuds facultatifs « description », « headers » et « body » que nous avons déjà décrits dans les paragraphes précédents.
VII-B-5. Le nÅ“ud « settings »▲
Le nœud « settings » est un nœud facultatif qui fournit des informations spécifiques au type de schéma de sécurité.
VII-B-5-a. Exemple pour le schéma OAuth 1.0▲
Le contenu du nœud « settings » est le suivant pour le schéma OAuth 1.0 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
#%RAML 1.0
title
:
My Sample API
securitySchemes
:
oauth_1_0
:
description
:
|
OAuth 1
.0
continue d′être supporté pour toutes les requêtes d′API mais OAuth 2
.0
est désormais préféré.
type
:
OAuth 1
.0
settings
:
requestTokenUri
:
https
:
//api.mysampleapi.com/1
/oauth/request_token
authorizationUri
:
https
:
//api.mysampleapi.com/1
/oauth/authorize
tokenCredentialsUri
:
https
:
//api.mysampleapi.com/1
/oauth/access_token
signatures
:
[
'HMAC-SHA1'
, 'PLAINTEXT'
]
Le nœud requestTokenUri contient l'URIUniform Resource Identifier du serveur auquel on demande le jeton.
Le nœud authorizationUri contient l'URIUniform Resource Identifier du serveur qui délivre l'autorisation de jeton.
Le nœud tokenCredentialsUri contient l'URIUniform Resource Identifier du serveur de ressource.
Le nœud signaturescontient une liste de signatures de méthodes utilisée par le serveur d'autorisation. Cette liste peut contenir les valeurs « HMAC-SHA1 », « RSA-SHA1 » ou « PLAINTEXT ». Si ce nœud est absent alors le serveur d'autorisation permettra l'utilisation des signatures de méthodes définies dans la spécification RFC5849 Section3.4.
VII-B-5-b. Exemple pour le schéma OAuth 2.0▲
Le contenu du nœud « settings » est le suivant pour le schéma OAuth 2.0 :
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.
#%RAML 1.0
title
:
Dropbox API
version
:
1
baseUri
:
https
:
//api.dropbox.com/{
version}
securitySchemes
:
oauth_2_0
:
description
:
|
Dropbox supporte OAuth 2
.0
pour authentifier toutes les requêtes de l′API
type
:
OAuth 2
.0
describedBy
:
headers
:
Authorization
:
description
:
|
Utilisé pour envoyer un jeton d′accès OAuth 2
.0
valide. À ne pas utiliser avec le paramètre "access_token"
type
:
string
queryParameters
:
access_token
:
description
:
|
Utilisé pour envoyer un jeton d′accès OAuth 2
.0
valide. À ne pas utiliser avec le header "Authorization"
.
type
:
string
responses
:
401
:
description
:
|
Jeton corrompu ou expiré. Ceci survient si l′utilisateur ou dropbox révoque ou expire un jeton d′accès. Pour solutionner ce problème, réauthentifier l′utilisateur.
403
:
description
:
|
Mauvaise requête OAuth (mauvaise clé, mauvais nonce ou temps écoulé). La réauthentification de l′utilisateur n′aidera en rien.
settings
:
authorizationUri
:
https
:
//www.dropbox.com/1
/oauth2/authorize
accessTokenUri
:
https
:
//api.dropbox.com/1
/oauth2/token
authorizationGrants
:
[
authorization_code, implicit ]
Le nœud contient l'URIUniform Resource Identifier du serveur qui délivre l'autorisation de jeton. Ce nœud est obligatoire seulement si on utilise l'autorisation via un code ou l'autorisation implicite (Voir le site : http://blog.netapsys.fr/oauth-comment-ca-marche/).
Le nœud contient l'URIUniform Resource Identifier du serveur qui fournit le jeton sur présentation de l'autorisation. Cette URIUniform Resource Identifier n'est pas utilisée pour les autorisations implicites puisqu'un jeton d'accès est délivré directement.
Le nœud contient une liste des types d'autorisations supportées par l'APIApplication Programming Interface. Il peut contenir les valeurs suivantes « authorization_code », « password », « client_credentials » ou encore « implicit ».
Un autre nœud est susceptible de figurer pour définir le schéma OAuth 2.0. Il s'agit du nœud scopes. Il spécifie précisément à quelles ressources l'utilisateur a le droit d'accéder.
VII-B-5-c. Exemple pour le schéma Basic Authentication▲
L'authentification basique ne requiert aucune information supplémentaire dans la définition de l'APIApplication Programming Interface :
2.
3.
4.
5.
6.
7.
8.
9.
#%RAML 1.0
title
:
Dropbox API
version
:
1
baseUri
:
https
:
//api.dropbox.com/{
version}
securitySchemes
:
basic
:
description
:
|
Cette API supporte l′authentification basique
type
:
Basic Authentication
VII-B-5-d. Exemple pour le schéma Digest Authentication▲
L'authentification « digest » ne requiert aucune information supplémentaire dans la définition de l'APIApplication Programming Interface :
2.
3.
4.
5.
6.
7.
8.
9.
#%RAML 1.0
title
:
Dropbox API
version
:
1
baseUri
:
https
:
//api.dropbox.com/{
version}
securitySchemes
:
digest
:
description
:
|
Cette API supporte l′authentification « digest »
type
:
Digest Authentication
VII-B-5-e. Exemple pour le schéma Pass Through▲
L'authentification « Pass Through » ne comporte pas de paramètre spécifique et son implémentation est connue de RAMLRESTful API Modeling Language. Vous devez fournir une valeur pour chaque « header » ou « queryParameter » défini dans le nœud « describedBy » et passé dans la requête :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
#%RAML 1.0
title
:
Dropbox API
version
:
1
baseUri
:
https
:
//api.dropbox.com/{
version}
securitySchemes
:
passthrough
:
description
:
|
Cette API supporte l′authentification « Pass Through »
type
:
Pass Through
describedBy
:
queryParameters
:
query
:
type
:
string
headers
:
api_key
:
type
:
string
VII-B-5-f. Exemple pour le schéma x-<other>▲
Les méthodes d'authentification « x-<other> » ne comportent pas de paramètre spécifique, car leur implémentation est méconnue en tant qu'implémentation standard de RAMLRESTful API Modeling Language.
Ces schémas de sécurité peuvent inclure uniquement les nœuds « description » et « describedBy » pour permettre de documenter l'utilisation attendue du schéma de sécurité :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
#%RAML 1.0
title
:
Custom API
version
:
1
baseUri
:
https
:
//api.custom.com/{
version}
securitySchemes
:
custom_scheme
:
description
:
|
Schéma de sécurité personnalisé pour authentifier des requêtes.
type
:
x-
custom
describedBy
:
headers
:
SpecialToken
:
description
:
|
Utilisé pour envoyer un jeton personnalisé.
type
:
string
responses
:
401
:
description
:
|
Mauvais jeton.
403
:
VII-C. Le nÅ“ud « securedBy »▲
Il s'agit d'un nœud permettant d'appliquer les schémas de sécurité définis dans le nœud « securitySchemes » à toutes les méthodes de l'APIApplication Programming Interface sauf à celles qui ont leur propre nœud « securedBy ».
Le schéma de sécurité spécifié dans une méthode est prioritaire par rapport au schéma de sécurité défini pour toute l'APIApplication Programming Interface ou pour une ressource dont dépend la méthode. Pour indiquer qu'une méthode est protégée en utilisant un schéma de sécurité spécifique, cette méthode doit obligatoirement être définie en utilisant le nœud « securedBy » ;
Exemple :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
#%RAML 1.0
title
:
Dropbox API
version
:
1
baseUri
:
https
:
//api.dropbox.com/{
version}
securedBy
:
[
oauth_2_0]
securitySchemes
:
oauth_2_0
:
!include securitySchemes/oauth_2_0.raml
oauth_1_0
:
!include securitySchemes/oauth_1_0.raml
/users
:
get
:
securedBy
:
[
oauth_2_0, oauth_1_0]
La valeur assignée au nœud « securedBy » doit être une liste de schémas de sécurité précédemment définis dans le nœud « securitySchemes » que nous avons expliqué plus haut.
Un nœud « securedBy » contenant « null » dans sa liste de schéma de sécurité (par exemple ) indique que la méthode peut être appelée sans appliquer de schéma de sécurité.
Comme nous l'avons évoqué plus haut, le nœud « securedBy » peut aussi appliquer une liste de schémas de sécurité à une ressource. Dans ce cas, toutes les méthodes à l'exception de celles utilisant leur propre nœud « securedBy » peuvent être authentifiées par l'un des schémas de sécurité spécifiés. La valeur du nœud « securedBy » dans la ressource passe outre la valeur du nœud « securedBy » au niveau de la racine. Les schémas de sécurité ne s'appliquent pas aux ressources imbriquées.
VIII. Création d'une bibliothèque RAML▲
Comme nous l'avons vu dans de précédents exemples, il est possible de faire appel à des bibliothèques RAMLRESTful API Modeling Language par l'utilisation du nœud « uses ».
Les bibliothèques sont utilisées pour combiner des ensembles de déclarations :
- de types de données ;
- de type de ressources ;
- des déclarations de « traits » ;
- des déclarations de schémas de sécurité.
dans un fichier qui sera réutilisable par différentes APIApplication Programming Interface RAMLRESTful API Modeling Language.
Exemple :
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.
#%RAML 1.0 Library
resourceTypes
:
Collection
:
get
:
responses
:
200
:
body
:
application/json
:
type
:
Vehicule[]
post
:
body
:
application/json
:
type
:
Vehicule
traits
:
pagination
:
usage
:
S′applique uniquement aux méthodes get
description
:
Permet de paginer les résultats
queryParameters
:
page
:
description
:
Numéro de page à récupérer
type
:
number
required
:
true
example
:
1
par_page
:
description
:
Nombre d′instances récupérées par page
type
:
integer
minimum
:
20
maximum
:
50
default
:
30
example
:
50
filtre_par_prix
:
usage
:
S′applique uniquement aux méthodes get
description
:
Permet de filtrer les réponses en fonction du prix
queryParameters
:
prixPlusPetitQue?:
description
:
Prix plus petit que
type
:
number
prixPlusGrandQue?:
description
:
Prix plus grand que
type
:
number
secured
:
usage
:
S′applique aux méthodes qui ont besoin d′être sécurisées
description
:
Certaines requêtes requièrent une authentification
headers
:
access_token
:
description
:
Access Token
example
:
5757gh76
required
:
true
types
:
Vehicule
:
(meta-
data):
Sur la déclaration du type "Vehicule"
properties
:
marque
:
type
:
string
required
:
true
(meta-
data):
Sur la déclaration de la "properties"
modele
:
type
:
string
required
:
true
categorie
:
type
:
string
required
:
true
enum
:
-
Citadine
-
berline
-
Monospace
-
SUV
places
:
integer
prix
:
required
:
false
type
:
number
couleur
:
required
:
true
enum
:
-
Blanc
-
Noir
-
Gris
-
Bordeaux
-
Jaune
-
Bleu
example
:
marque
:
RENAULT
modele
:
Scenic
categorie
:
Monospace
places
:
7
prix
:
31500
couleur
:
Gris
Conducteur
:
properties
:
nom
:
type
:
string
required
:
true
prenom
:
type
:
string
required
:
true
date_naissance
:
type
:
date-
only
required
:
true
example
:
1981
-
05
-
04
example
:
nom
:
Célère
prenom
:
Jacques
date_naissance
:
1981
-
05
-
04
L'utilisation de cette bibliothèque se résume à l'utilisation du nœud « uses » :
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.
#%RAML 1.0
title
:
API de gestion d′un parc automobile
uses
:
NewLibrary
:
NewLibrary.raml
description
:
API permettant de gérer les opérations CRUD (create, read, update, delete) relatives la gestion d′un parc de véhicules
version
:
v1
baseUri
:
https
:
//{
ville}
.garage.com/{
release}
baseUriParameters
:
ville
:
description
:
Sous-
domaine auquel l′API est accessible.
enum
:
[
paris, dijon]
release
:
description
:
Version de l′API
protocols
:
[
HTTP, HTTPS ]
mediaType
:
[
application/json, application/xml ]
documentation
:
-
title
:
Accueil
content
:
Bienvenue sur la documentation de l′API de gestion de parc automobile
-
title
:
Legal
content
:
!include docs/legal.markdown
annotationTypes
:
deprecie
:
nil
experimental
:
string? #équivalent à « nil | string »
auteur
:
string #string est le type par défaut
#Deux autres syntaxes possibles sont :
#auteur:
# type: string
#ou
#auteur:
niveau_autorisation
:
allowedTargets
:
[
Resource ]
properties
:
niveau
:
enum
:
[
faible, moyen, élevé ]
required
:
true
signature
:
pattern
:
"\\d{3}-\\w{12}"
required
:
true
meta-
resource-
method
:
allowedTargets
:
[
Resource, Method ]
meta-
data
:
allowedTargets
:
TypeDeclaration
traits
:
types
:
/vehicules
:
is
:
[
NewLibrary.secured ]
#Les méthodes utilisées par la ressource « /vehicules » seront toutes sécurisées
displayName
:
vehicules
(meta-
resource-
method):
Sur une ressource
type
:
NewLibrary.Collection
get
:
is
:
[
NewLibrary.filtre_par_prix, NewLibrary.pagination ]
/conducteurs
:
displayName
:
conducteurs
(niveau_autorisation):
niveau
:
élevé
signature
:
230
-
ghtwvfrs1itr
get
:
(deprecie):
(experimental):
responses
:
200
:
body
:
application/json
:
type
:
NewLibrary.Conducteur[]
/{
id}:
put
:
description
:
Met à jour un conducteur dont l′identifiant est fourni dans l′uri
body
:
application/json
:
type
:
NewLibrary.Conducteur
delete
:
responses
:
204
:
Dans l'exemple ci-dessus, remarquez que, dans le but d'utiliser les « types », « resourceTypes », et autres « traits » définis dans la bibliothèque, il est nécessaire de préfixer le nom de ces entités par le nom de la bibliothèque. Par exemple, pour utiliser le type « Conducteur » défini dans la bibliothèque, il est nécessaire d'écrire : « NewLibrary.Conducteur ».
IX. Utilisation d'un IDE (Integrated Development Environment)▲
Nous savons désormais comment écrire des spécifications RAMLRESTful API Modeling Language. Sachez maintenant que des outils tels « API Workbench » ou « API Designer » peuvent vous aider à la rédaction de telles spécifications en proposant notamment des fonctionnalités de création de projet RAMLRESTful API Modeling Language, de navigation, d'autocomplétion et surtout de validation.
Attardons-nous sur l'IDEIntegrated Development Environment « API Workbench » non pas parce que « API Designer » est moins bien, mais parce que « API Workbench » est le plus répandu.
Cet IDEIntegrated Development Environment supporte les versions « 0.8 » et « 1.0 » de RAMLRESTful API Modeling Language. Il permet non seulement de concevoir et rédiger les spécifications, mais aussi de fournir une documentation claire et de partager des APIApplication Programming Interface RESTRepresentational State Transfer à l'aide de « GIT ».
IX-A. Installation de « API Workbench »▲
« API Workbench » s'intègre parfaitement dans l'éditeur de texte « ATOM ». La version « Windows » de « ATOM » est téléchargeable à cette adresse : https://atom.io/.
-
Une fois « ATOM » installé, ouvrez une invite de commande « Windows » puis déplacez-vous dans le répertoire « C:\Users\votre_profil\AppData\Local\atom\bin » comme le présente la capture d'écran ci-dessous :
-
Si votre ordinateur se situe derrière un proxy, saisissez la commande suivante :
apm config set proxy http://proxy.truc.fr:numero_port
où la chaîne de caractères « http://proxy.truc.fr » devra être remplacée par l'adresse du proxy et « numero_port » devra être remplacée par le numéro du port permettant d'accéder au proxy.
La commande « apm » (pour « Atom Package Manager ») a été installée lors de l'installation d'Atom. -
Saisissez ensuite la commande :
apm install api-workbench -
À ce stade, lorsque vous ouvrez « ATOM », dans le menu « Packages », vous devriez avoir le sous-menu « API Workbench » comme le présente l'image ci-dessous :
- Ceci confirme que l'IDEIntegrated Development Environment « API Workbench » a bien été installé.
IX-B. Utilisation de « API Workbench »▲
Étant donné qu'un exemple vaut mieux qu'un long discours, vous trouverez, sur le site officiel de « API Workbench », à l'adresse http://apiworkbench.com/docs/, un exemple d'utilisation de l'outil, à suivre pas-à -pas, et qui traite de la création de la plupart des nœuds que nous avons étudiés en première partie de ce tutoriel.
Vous verrez notamment, à travers ces exemples, que « API Workbench » vous permet au maximum d'éviter d'écrire du code et vous épargne ainsi toute erreur de syntaxe.
X. Documentation des spécifications RAML▲
Maintenant que nous savons rédiger des spécifications RAMLRESTful API Modeling Language, à l'aide de l'IDEIntegrated Development Environment « API Workbench », il est désormais important de considérer un outil qui permet de générer la documentation de notre APIApplication Programming Interface.
L'objectif de cette documentation, pour les utilisateurs de notre APIApplication Programming Interface, est de visualiser, de manière rapide et claire, les spécifications que nous avons écrites.
L'IDEIntegrated Development Environment « API Workbench » permet déjà de visualiser rapidement la documentation d'une APIApplication Programming Interface RESTRepresentational State Transfer. Pour cela, il suffit, dans le menu « Packages » de « ATOM », de sélectionner le sous-menu « API Workbench » puis « Open API Console » comme le présente la capture d'écran ci-dessous :
Dès lors, la vue ci-dessous apparaîtra :
Néanmoins, il existe des outils plus puissants qui permettent de documenter une APIApplication Programming Interface, mais aussi de tester son implémentation à partir des spécifications. C'est ce que nous allons voir avec « API Console ».
Il existe plusieurs outils de génération de documentation à partir de spécifications RAMLRESTful API Modeling Language dont les plus connus sont les suivants :
- API Console ;
- RAML2HTML.
RAML2THML ne semble pas encore supporter RAMLRESTful API Modeling Language 1.0. De plus, son développement semble s'être arrêté (momentanément ?) à la version 0.8 de RAMLRESTful API Modeling Language.
Pour cette raison, nous nous limiterons, ici, à utiliser l'outil « API Console ».
En outre, « API Console » permet à l'utilisateur de l'APIApplication Programming Interface (le développeur) d'interagir avec l'APIApplication Programming Interface depuis l'intérieur de la documentation. En effet, c'est un outil qui construit lui-même les formulaires à partir des paramètres à saisir lus dans les spécifications. Il valide les données saisies en fonction des définitions qu'il trouve dans l'APIApplication Programming Interface. Il permet également de tester l'authentification.
« API Console » permet de générer une documentation au format HTML à partir des spécifications RAMLRESTful API Modeling Language passées en paramètre. En raison du format HTML utilisé en sortie, il est possible de se déplacer facilement dans la documentation et de lire de manière claire les caractéristiques de l'APIApplication Programming Interface RESTRepresentational State Transfer.
X-A. Utilisation rapide de « API Console »▲
La procédure permettant de générer la documentation est la suivante :
-
Téléchargez « API Console » à l'adresse https://github.com/mulesoft/api-console en cliquant sur le bouton vert nommé « Clone or download ».
-
Il est possible d'utiliser directement l'outil en dézippant l'archive téléchargée puis en ouvrant le fichier « index.html » situé dans le répertoire « dist ». La fenêtre suivante apparaît :
-
Dès lors, vous avez deux choix :
- soit vous renseignez le champ « INITIALIZE FROM THE URL OF A RAML FILE » à partir d'une URL du type « http://…/fichier.raml » ;
-
soit vous renseignez le champ « OR PARSE RAML IN HERE ».
Sachez toutefois que le premier choix ne fonctionnera pas si « API Console » n'est pas installé sur un serveur d'application. Nous verrons l'installation de « API Console » sous Tomcat plus loin dans ce paragraphe.
-
Nous choisissons de renseigner la section « OR PARSE RAML IN HERE » en faisant un « copier-coller » du contenu de notre fichier RAML comme le présente la capture d'écran ci-dessous :
L'utilisation de la fonctionnalité « OR PARSE RAML IN HERE » ne permet pas d'inclure des bibliothèques ou des schémas JSON ou XML.
- Cliquez ensuite sur le bouton « Load RAML ». Vous obtiendrez une documentation du type de celle présentée ci-dessous avec une jolie mise en page où toutes les zones sont cliquables :
Sur cette dernière capture d'écran, on remarque le bouton « POST ». En effet, comme nous l'avons vu en introduction de ce paragraphe, « API Console » fournit des formulaires permettant de tester les différentes fonctionnalités de l'implémentation de notre APIApplication Programming Interface à partir de la documentation générée.
Ainsi, via cette capture d'écran, on peut voir que « API Console » a généré un formulaire permettant la saisie de véhicules. Les tests de notre service web RESTRepresentational State Transfer sont donc facilement faisables via « API Console ».
X-B. Utilisation de « API Console » via un serveur d'application▲
Nous avons vu, dans le paragraphe ci-dessus, qu'il est possible d'utiliser « API Console » de manière rapide en dézippant l'archive et en ouvrant le fichier « index.html ».
Cette solution simple permet de vérifier rapidement la consistance des spécifications de l'APIApplication Programming Interface que vous souhaitez développer en utilisant la section « OR PARSE RAML IN HERE ». L'utilisation de cette section souffre toutefois de quelques limitations.
Afin de profiter de toutes les fonctionnalités de « API Console » (utilisation des bibliothèques, import de schémas JSON et XML, possibilité de lancer « API Console » directement avec le fichier « .raml » souhaité, il est nécessaire d'installer l'outil « API Console » sur un serveur d'application.
Dans notre cas, nous utiliserons « Tomcat » dans sa version « 8.5.5 ».
L'installation de Tomcat est très simple. Il suffit de procéder comme suit :
-
Allez sur le site http://tomcat.apache.org/ et dans la page qui apparaît, dans la section « Download » du menu de gauche, choisissez « Tomcat 8 » (nous nous abstenons, ici, d'utiliser la version 9 de « Tomcat », car au moment de la rédaction de cet article, il s'agit encore d'une version « Bêta »).
-
Dans la page qui apparaît, et dans le cas où, comme nous, vous travaillez sous « Windows », cliquez sur le lien «  32-bit/64-bit Windows Service Installer (pgp, md5, sha1) » dans la section « Binary Distributions » :
-
Installez « Tomcat » en cliquant sur l'exécutable téléchargé.
-
Après son installation « Tomcat » est immédiatement opérationnel. Pour vous en assurer, ouvrez un navigateur Internet et saisissez l'adresse : http://localhost:8080 . La page suivante apparaît :
-
Souvenez-vous de l'archive contenant « API Console » que nous avons téléchargée dans la section précédente.
Une fois cette archive dézippée, placez le répertoire « dist », avec tous les sous-répertoires qu'il contient, dans le répertoire « C:\Program Files (x86)\Apache Software Foundation\Tomcat 8.5\webapps\ ». Il s'agit du répertoire par défaut dans lequel « Tomcat » gère ses applications si vous n'avez pas modifié les options lors de l'installation de « Tomcat ».
Le chemin vers le répertoire « webapps » est à modifier si vous avez modifié les paramètres par défaut lors de l'installation de « Tomcat ». - Dès lors, si, dans votre navigateur préféré, vous saisissez l'URL http://localhost:8080/dist, la page d'accueil de « API Console » apparaîtra :
Il est désormais possible d'utiliser la section « INITIALIZE FROM THE URL OF A RAML FILE » en y renseignant l'URL d'un fichier RAML comme le présente la capture d'écran ci-dessous.
Cette section, contrairement à la section « OR PARSE RAML IN HERE », permet l'appel, dans les spécifications RAML, de bibliothèques ou de schémas JSON ou XML :
À noter que « API Console » fournit un ensemble d'exemples dans le répertoire « dist/examples/ ». C'est à l'un de ces exemples que nous faisons appel dans la capture d'écran ci-dessus.
Un clic sur le bouton « Load from URL » provoque alors l'affichage de la documentation constituée à partir du fichier RAMLRESTful API Modeling Language passé en paramètre :
Néanmoins, si vous souhaitez modifier votre fichier RAMLRESTful API Modeling Language après avoir visualisé sa documentation, il est nécessaire de rafraîchir la page pour revenir à la page de saisie de l'URL du fichier puis de ressaisir cette URL.
Pour éviter cet inconvénient, sachez que vous pouvez saisir directement l'URL de votre fichier RAMLRESTful API Modeling Language dans la barre d'adresse sous la forme suivante :
http://localhost:8080/dist/?raml=http://localhost:8080/dist/examples/parc.raml
Comme vous pouvez le voir, cette URL est composée d'une partie fixe contenant le paramètre « raml » (« http://localhost:8080/dist/?raml= ») et d'une partie variable contenant l'URL du fichier RAMLRESTful API Modeling Language placé sur le serveur d'application (dans le répertoire « dist » ou dans l'un de ses sous-répertoires).
Les bibliothèques et autres schémas « JSON » ou « XML » appelés dans l'APIApplication Programming Interface doivent être également placés dans le répertoire « dist » ou dans l'un de ses sous-répertoires.
XI. Code source de l'exemple▲
Les fichiers RAMLRESTful API Modeling Language sur lesquels nous avons basé nos exemples sont disponibles ici : https://olivier-rozier.developpez.com/tutoriels/rest/raml/fichiers/exempleRAML.zip
XII. Les alternatives à RAML▲
Les langages et les projets de documentation d'APIApplication Programming Interface les plus populaires sont « RAMLRESTful API Modeling Language », « Swagger », « Slate » et « API BluePrint ». Il existe également une autre alternative telle « WADLWeb Application Description Language ».
Nous nous attacherons, dans ce paragraphe, à décrire le principe d'utilisation des alternatives à RAMLRESTful API Modeling Language puis à comparer plus en détail RAMLRESTful API Modeling Language avec l'alternative la plus populaire qu'est « Swagger ».
XII-A. OpenAPI Specification (Swagger)▲
Tout comme RAMLRESTful API Modeling Language, le but de « Swagger » est de générer la documentation d'APIApplication Programming Interface RESTRepresentational State Transfer compréhensible à la fois par l'ordinateur et par l'humain afin d'assimiler les capacités d'un service sans pour autant devoir lire le code source. La rédaction de ces spécifications peut s'effectuer en YAML ou en JSONJavaScript Object Notation.
Les spécifications écrites sous « Swagger » permettent de définir un ensemble de fichiers décrivant une APIApplication Programming Interface. Le projet « Swagger-UI » utilise ensuite ces fichiers pour générer une jolie documentation.
Il est ensuite possible d'utiliser « Swagger-Codegen » pour générer à la fois les bibliothèques client et implémenter le côté serveur dans différents langages.
Pour plus d'informations concernant « Swagger », n'hésitez pas à consulter le site officiel : http://swagger.io/
XII-B. API BluePrint▲
« API BluePrint » s'appuie uniquement sur le langage markdown. Contrairement à RAMLRESTful API Modeling Language et à « Swagger », « API BluePrint » requiert l'installation d'un outil permettant d'interpréter un fichier BluePrint et de générer des fichiers HTML statiques. Parmi ces outils, nous pouvons citer « Apiary », « Dredd », « Drafter »…
C'est le principal inconvénient de « API BluePrint » : il manque d'outils avancés et de générateur de code permettant de tester le bien-fondé des spécifications ou de générer l'API correspondante. C'est la principale raison de son adoption très lente par rapport à « Swagger » et à RAMLRESTful API Modeling Language.
XII-C. WADL▲
« WADLWeb Application Description Language » est un langage basé sur le XML et qui a été présenté au W3CWorld Wide Web Consortium par Sun Microsystem en 2009 pour décrire les interactions avec un service web RESTRepresentational State Transfer. Néanmoins, le consortium n'a toujours pas souhaité engager une standardisation de ce langage.
Dans un fichier WADLWeb Application Description Language, des balises « resource » décrivent les ressources accessibles et englobent des balises « method », permettant de décrire les méthodes d'accès à ces ressources. Les balises « method » contiennent, par l'intermédiaire d'une balise « request », la description des paramètres en entrée de ces méthodes. Une balise « response » précise le type de réponse attendu (xml, json…) L'ensemble de ces balises permet donc de décrire les requêtes et les réponses liées à une ressource.
On peut dire que « WADLWeb Application Description Language » est l'équivalent, côté service web, du WSDLWeb Services Description Language pour les services web SOAPSimple Object Access Protocol.
Il s'agit d'un langage assez « pompeux » pour créer des descriptions et pas toujours très compréhensible aux néophytes par rapport à RAMLRESTful API Modeling Language , « Swagger » ou « API BluePrint ».
La différence majeure entre RAML et les WSDL réside dans le fait que le contrat (ou la spécification de l'API) définit le « template » des URI utilisées en tant que « endpoint », mais également le format et la structure des données échangées lors de chaque requête.
En fonction des besoins du client, on peut se contenter de spécifier les formats d'échanges des données (JSON ou XML) ou uniquement quelques exemples d'échanges. Ces informations peuvent suffire à un développeur pour créer un client tout en négligeant le serveur. Contrairement à WSDL, il n'y a pas, en RAMLRESTful API Modeling Language, de contrat à proprement parler entre le client et le serveur : les clients ne sont pas couplés à un endpoint. Ils sont couplés à un type de média (JSON, XML…) transféré entre le client et le serveur. Avec RAML, on ne se soucie pas de la couche transport contrairement à WSDL.
XII-D. Slate▲
« Slate » utilise le langage markdown. Il permet uniquement de générer la documentation de l'APIApplication Programming Interface puis l'encapsule dans une page HTML statique qui, par conséquent, est très simple à héberger.
« Slate » est une alternative intéressante, car il constitue un bon compromis entre la syntaxe, la facilité d'utilisation et la présentation.
Néanmoins, il est intéressant de remarquer que « Slate », tout comme « API BluePrint », n'utilise pas un langage de description interprétable et sur lequel peuvent être générées des interfaces de tests comme c'est le cas pour RAMLRESTful API Modeling Language et « Swagger ».
Pour plus d'informations concernant « Slate », n'hésitez pas à consulter le site officiel : https://github.com/lord/slate
XII-E. Comparaison entre RAML et « Swagger »▲
RAMLRESTful API Modeling Language et « Swagger » constituent les deux principales solutions de documentation d'APIApplication Programming Interface RESTRepresentational State Transfer.
La qualité d'une API sera avant tout caractérisée par la qualité de sa documentation. C'est au développeur de choisir le langage ou le framework qui est le plus approprié à son objectif.
Avec « Swagger », la génération de SDKSoftware Development Kit est intégrée dans le système, notamment par le support de nombreux langages de programmation. Si l'objectif désiré est l'élaboration d'un SDKSoftware Development Kit pour l'interaction client, « Swagger » est la solution à retenir.
En revanche, si la génération de SDKSoftware Development Kit est gérée via des applications tierces ou que vous désirez utiliser une méthodologie bien particulière (voir par exemple le tutoriel « Tutoriel pour développer et déployer un service Web RESTFUL OSGI multibundle sous Eclipse et Karaf » dans lequel on divise le service web RESTRepresentational State Transfer en couches), ou que vous souhaitez faire participer des développeurs et des non-développeurs à la documentation, RAMLRESTful API Modeling Language est fait pour vous. En effet, même si de nombreux développeurs préfèrent le format JSONJavaScript Object Notation, RAMLRESTful API Modeling Language permet aux développeurs et aux non-développeurs de lire et de mettre à jour la spécification. RAMLRESTful API Modeling Language est donc parfait pour concevoir une API à partir de rien ou pour rendre la documentation plus lisible.
Au niveau de l'outil d'édition, RAMLRESTful API Modeling Language et « Swagger » disposent tous les deux d'éditeurs intuitifs possédant une console. Cette console fournit un aperçu montrant exactement à quoi ressemblera le produit fini en fonction du contenu de l'éditeur.
En ce qui concerne la génération d'une APIApplication Programming Interface à partir de ses spécifications, si vous avez utilisé RAMLRESTful API Modeling Language, vous pouvez utiliser « Mule Studio » avec « Apikit » tel que montré à l'adresse : https://docs.mulesoft.com/apikit/apikit-tutorial. Il s'agit de la méthode la plus rapide pour construire et déployer un projet depuis les spécifications RAMLRESTful API Modeling Language.
De manière plus poussée, vous pouvez utiliser l'outil « raml-for-jax-rs » pour passer de spécifications RAMLRESTful API Modeling Language à l'implémentation d'interfaces JAVA utilisant les annotations JAX-RS ou inversement (voir la page https://github.com/mulesoft/raml-for-jax-rs).
En plus de JAX-RS, un grand nombre de langages de programmation sont désormais disponibles. Pour plus de renseignements, n'hésitez pas à visiter la page : http://raml.org/developers/build-your-api
« Swagger », quant à lui, possède un outil appelé « Swagger Codegen » pour développer votre API dans le langage de programmation désiré parmi ceux répertoriés dans le lien suivant : https://github.com/swagger-api/swagger-codegen#swagger-codegen-core-team.
Il est à noter que les outils disponibles pour « Swagger » prennent en charge plus de langages de programmation que les outils disponibles pour RAMLRESTful API Modeling Language.
XIII. Conclusion▲
Dans ce tutoriel, nous avons vu que RAMLRESTful API Modeling Language constitue un moyen de décrire les API RESTful de manière tout à fait compréhensible pour un développeur et même pour un analyste grâce à l'utilisation d'IDEIntegrated Development Environment tel « API Workbench ».
Une spécification RAMLRESTful API Modeling Language représente un contrat qui reflète la structure de l'API tout en étant indépendant de son implémentation.
Il est ainsi possible de distinguer rapidement :
- les types utilisés ainsi que les caractéristiques qui les composent ;
- les ressources à appeler ;
- les méthodes pouvant être utilisées sur les ressources.
En plus de ces avantages, RAMLRESTful API Modeling Language encourage la réutilisabilité du code (via les bibliothèques) et l'utilisation de « pattern ».
Je tiens à remercier Mickaël Baron pour la relecture technique de cet article et f-leb pour la relecture orthographique.