Arthur Noseda
Marié, 2 enfants
Développeur Java depuis 2003
Vous n’avez peut-être pas besoin de respecter les contraintes HATEOAS.
-
Roy Fielding est l’un des principaux auteurs de la spécification HTTP.
-
Membre fondateur de la fondation Apache
-
A défini le style d’architecture representational state transfer
-
…
David Heinemeier Hansson, le créateur de Ruby on Rails, démonte un certain nombre d’idées reçues à propos d’hypermedia.
[*] --> Draw state NextRound <<choice>> state HeroWoundLuckTested <<choice>> state MonsterWoundLuckTested <<choice>> Draw --> NextRound : Next Round NextRound --> HeroWound : [har < mar && hs > 0] NextRound --> HeroDied : [har < mar && hs <= 0] NextRound --> MonsterWound : [har > mar && ms > 0] NextRound --> MonsterDied : [har > mar && ms <= 0] NextRound --> Draw : [har == mar] HeroWound --> NextRound : Next Round HeroWound --> HeroWoundLuckTested : Test Luck HeroWoundLuckTested --> NextRound : [hs > 0] HeroWoundLuckTested --> HeroDied : [hs <= 0] MonsterWound --> NextRound : Next Round MonsterWound --> MonsterWoundLuckTested : Test Luck MonsterWoundLuckTested --> NextRound : [ms > 0] MonsterWoundLuckTested --> MonsterDied : [ms <= 0] HeroDied --> [*] MonsterDied --> [*]
Au moment de présenter "Justice Will Take Us Millions Of Intricate Moves", Richardson travaillait sur l’application Launchpad pour Canonical.
Jason Desrosiers redécoupe le niveau 3 en 4
-
HMM 0 :
application/json
contenant des URL -
HMM 1 : type de médias formalisant ce que sont les liens
-
HMM 2 : HMM 1 + type de médias formalisant comment mettre à jour une ressource
-
HMM 3 : HMM 2 + type de médias utilisant un vocabulaire dont la sémantique est décrite et partagée
Système de présentation de l’information reposant sur des hyperliens qui permettent de passer d’un document multimédia à un autre.
Qui produit des API hypermedia ?
-
2000 - La dissertation de Roy Fielding
-
2008 - Maturity Heuristic (Richardson)
-
19/07/2012 - Spring HATEOAS 0.1.0.RELEASE
-
30/09/2019 - Spring HATEOAS 1.0.0.RELEASE
Le titre exact de la dissertation de Roy Fielding : Architectural Styles and the Design of Network-based Software Architectures
-
EntityModel
-
CollectionModel
-
Link
-
Affordance
@GetMapping
public CollectionModel<EntityModel<Todo>> getTodos() {
return CollectionModel.of(todoRepository.findAll().stream()
.map(this::toRepresentation)
.collect(Collectors.toList()),
linkTo(methodOn(TodoController.class)
.getTodos()).withSelfRel()
.andAffordance(afford(methodOn(TodoController.class)
.createTodo(null))));
}
@GetMapping("/{id}")
public EntityModel<Todo> getTodo(@PathVariable("id") UUID id) {
return todoRepository.findById(id)
.map(this::toRepresentation)
.orElseThrow(this::notFound);
}
private EntityModel<Todo> toRepresentation(Todo todo) {
return EntityModel.of(todo,
linkTo(methodOn(TodoController.class)
.getTodo(todo.getId())).withSelfRel()
.andAffordance(afford(methodOn(TodoController.class)
.updateTodo(todo.getId(), null)))
.andAffordance(afford(methodOn(TodoController.class)
.deleteTodo(todo.getId()))));
}
IANA : application/hal+json
et application/hal+xml
-
Le plus simple et le plus populaire
-
Rien de prévu pour les modifications
L’Internet Assigned Numbers Authority supervise entre autres l’allocation globale des adresses IP, la gestion de la zone racine dans les DNS et les types de médias.
{
"_embedded" : {
"todos" : [ {
"id" : "5f4bb924-e930-4b2d-ae1c-4bdb501752ca",
"title" : "Go on a Treasure Hunt",
"completed" : false,
"_links" : {
"self" : {
"href" : "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca"
}
}
}, ... ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/todos"
}
}
}
IANA : application/prs.hal-forms+json
-
Ajoute la notion de formulaire (au sens HTML) à HAL
prs
signifie personal.
{
"_embedded" : {
"todos" : [ {
"id" : "5f4bb924-e930-4b2d-ae1c-4bdb501752ca",
"title" : "Go on a Treasure Hunt",
"completed" : false,
"_links" : {
"self" : {
"href" : "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca"
}
},
"_templates" : {
"default" : {
"method" : "PUT",
"properties" : [ {
"name" : "completed",
"readOnly" : true
}, {
"name" : "title",
"readOnly" : true,
"type" : "text"
} ]
},
"deleteTodo" : {
"method" : "DELETE",
"properties" : [ ]
}
}
}, ... ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/todos"
}
},
"_templates" : {
"default" : {
"method" : "POST",
"properties" : [ {
"name" : "title",
"readOnly" : true,
"type" : "text"
} ]
}
}
}
{
"id" : "5f4bb924-e930-4b2d-ae1c-4bdb501752ca",
"title" : "Go on a Treasure Hunt",
"completed" : false,
"_links" : {
"self" : {
"href" : "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca"
}
},
"_templates" : {
"default" : {
"method" : "PUT",
"properties" : [ {
"name" : "completed",
"readOnly" : true
}, {
"name" : "title",
"readOnly" : true,
"type" : "text"
} ]
},
"deleteTodo" : {
"method" : "DELETE",
"properties" : [ ]
}
}
}
-
Une application Angular développée par Kai Tödter
-
Supporte HAL et HAL-FORMS
IANA : application/vnd.collection+json
vnd
signifie vendor.
{
"links": [
{
"rel": "self",
"href": "http://localhost:8080/todos"
}
],
"content": [
{
"id": "6f0cd0d2-a870-4c84-a76a-498014779c21",
"title": "Go on a Treasure Hunt",
"completed": false,
"links": [
{
"rel": "self",
"href": "http://localhost:8080/todos/6f0cd0d2-a870-4c84-a76a-498014779c21"
}
]
},
...
]
}
application/vnd.amundsen-uber+json
-
Pas enregistré auprès de l’IANA
{
"uber": {
"version": "1.0",
"data": [
{
"name": "self",
"rel": [
"self",
"getTodos"
],
"url": "http://localhost:8080/todos"
},
{
"name": "createTodo",
"rel": [
"createTodo"
],
"url": "http://localhost:8080/todos",
"action": "append",
"model": "title={title}"
},
{
"data": [
{
"name": "self",
"rel": [
"self",
"getTodo"
],
"url": "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca"
},
{
"name": "updateTodo",
"rel": [
"updateTodo"
],
"url": "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca",
"action": "replace",
"model": "completed={completed}&title={title}"
},
{
"name": "deleteTodo",
"rel": [
"deleteTodo"
],
"url": "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca",
"action": "remove",
"model": ""
},
{
"name": "todo",
"data": [
{
"name": "completed",
"value": false
},
{
"name": "id",
"value": "5f4bb924-e930-4b2d-ae1c-4bdb501752ca"
},
{
"name": "title",
"value": "Go on a Treasure Hunt"
}
]
}
]
},
...
]
}
}
{
"uber": {
"version": "1.0",
"data": [
{
"name": "self",
"rel": [
"self",
"getTodo"
],
"url": "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca"
},
{
"name": "updateTodo",
"rel": [
"updateTodo"
],
"url": "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca",
"action": "replace",
"model": "completed={completed}&title={title}"
},
{
"name": "deleteTodo",
"rel": [
"deleteTodo"
],
"url": "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca",
"action": "remove",
"model": ""
},
{
"name": "todo",
"data": [
{
"name": "completed",
"value": false
},
{
"name": "id",
"value": "5f4bb924-e930-4b2d-ae1c-4bdb501752ca"
},
{
"name": "title",
"value": "Go on a Treasure Hunt"
}
]
}
]
}
}
application/alps+json
et application/alps+xml
@GetMapping(value = "/profile", produces = ALPS_JSON_VALUE)
public Alps alps() {
return Alps.alps()
.descriptor(
List.of(
Descriptor.builder()
.id("id")
.type(Type.SEMANTIC)
.doc(Doc.builder().value("The identifier").build())
.build(),
Descriptor.builder()
.id("title")
.type(Type.SEMANTIC)
.doc(Doc.builder().value("The title").build())
.build(),
// ici les autres propriétés
)
)
.build();
}
IANA : application/problem+json
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Problem.create()
.withType(URI.create("http://example.com/todos/cannot-update-completed"))
.withTitle("Cannot update completed")
.withStatus(HttpStatus.FORBIDDEN)
.withInstance(URI.create(String.format("/todos/%s", id))));
IANA : application/vnd.api+json
https://github.com/toedter/spring-hateoas-jsonapi (Kai Tödter)
{
"data": [
{
"id": "5f4bb924-e930-4b2d-ae1c-4bdb501752ca",
"type": "todos",
"attributes": {
"title": "Go on a Treasure Hunt",
"completed": false
},
"links": {
"self": "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca"
}
},
...
],
"links": {
"self": "http://localhost:8080/todos"
}
}
IANA : application/vnd.siren+json
https://github.com/ingogriebsch/spring-hateoas-siren (Ingo Griebsch)
{
"class": [
"collection"
],
"entities": [
{
"class": [
"entity"
],
"rel": [
"item"
],
"properties": {
"id": "5f4bb924-e930-4b2d-ae1c-4bdb501752ca",
"title": "Go on a Treasure Hunt",
"completed": false
},
"links": [
{
"rel": [
"self"
],
"href": "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca"
}
],
"actions": [
{
"name": "updateTodo",
"method": "PUT",
"href": "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca",
"type": "application/x-www-form-urlencoded",
"fields": [
{
"name": "completed",
"type": "text"
},
{
"name": "title",
"type": "text"
}
]
},
{
"name": "deleteTodo",
"method": "DELETE",
"href": "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca"
}
]
},
...
],
"links": [
{
"rel": [
"self"
],
"href": "http://localhost:8080/todos"
}
],
"actions": [
{
"name": "createTodo",
"method": "POST",
"href": "http://localhost:8080/todos",
"type": "application/x-www-form-urlencoded",
"fields": [
{
"name": "title",
"type": "text"
}
]
}
]
}
{
"class": [
"entity"
],
"properties": {
"id": "5f4bb924-e930-4b2d-ae1c-4bdb501752ca",
"title": "Go on a Treasure Hunt",
"completed": false
},
"links": [
{
"rel": [
"self"
],
"href": "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca"
}
],
"actions": [
{
"name": "updateTodo",
"method": "PUT",
"href": "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca",
"type": "application/x-www-form-urlencoded",
"fields": [
{
"name": "completed",
"type": "text"
},
{
"name": "title",
"type": "text"
}
]
},
{
"name": "deleteTodo",
"method": "DELETE",
"href": "http://localhost:8080/todos/5f4bb924-e930-4b2d-ae1c-4bdb501752ca"
}
]
}
$ curl -I http://localhost:8080/actuator
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Content-Length: 1735
Date: Tue, 22 Mar 2022 20:42:01 GMT
-
Documenté : https://docs.spring.io/spring-boot/docs/current/actuator-api/
-
Pas enregistré auprès de l’IANA
-
Architectural Styles and the Design of Network-based Software Architectures (Roy T. Fielding, Ph.D.)
-
A little REST and Relaxation (Roy T. Fielding, Ph.D.)
-
Roy Fielding on Versioning, Hypermedia, and REST (Mike Amundsen)
-
Spring Restbucks (Oliver Drotbohm)
-
Quelles technologies pour faire des APIs hypermedia ? (Fabernovel)
-
RESTful hypermedia APIs: Useful or not? (Kai Tödter)
-
Evolving architecture with DDD and hypermedia (Einar Høst)