Échange de données 1

REST propose une architecture standard pour l'élaboration d'application serveur

  • Interface uniforme pour l'accès aux ressources
  • Séparation client-serveur

Le protocole HTTP offre plusieurs mécanismes permettant de respecter les principes REST en développant un service web respecter les principes REST en développant un service web

  • URL
  • Verbe
  • Status
  • Headers
  • Body

Requête
Réponse

URL

La structure des URLs permet d'identifier clairement et de manière uniforme les ressources accessible via le service web.

/products
/products?brand=keychron&sort=price-asc
/products/B09NLTWKGP

/semesters
/semesters/A2020
/semesters/A2020/courses
/semesters/A2020/courses/420-0Q7-SW
/semesters/A2020/courses/420-0Q7-SW/groups
/semesters/A2020/courses/420-0Q7-SW/groups/2

/orders/202201012314
/orders/202201012314/invoice

Verbe

Les verbes HTTP permettent de rendre explicite l'action à appliquer sur une ressource

  • GET Récupérer
  • POST Créer, soumettre
  • PUT Remplacer sinon créer
  • PATCH Modifier partiellement
  • DELETE Supprimer
GET /conversations

GET /conversations/9812
GET /conversations/9812/messages

POST /conversations/9812/messages
{ "text": ... }

PATCH /messages/987123654
{ "text" : ... }

PATCH /messages/987123654
{ "reaction" : ... }

DELETE /messages/456192837

Verbes

Status

  • 1XX Information
  • 2XX Succès `200, 201, 202, 204`
  • 3XX Redirection
  • 4XX Erreur du client `400, 401, 403, 404, 405, 429`
  • 5XX Erreur du serveur

Status Codes

ATTENTION

Il est aussi possible de TOUJOURS retourner un code 200 et d'indiquer l'erreur dans le body de la réponse.

Headers

Les headers permettent d'ajouter des informations complémentaires à la communication HTTP, requête ou réponse, identifiés par la structure Clé: Valeur

  • Contenu
  • Authentification
  • Contexte requête/réponse
  • Cookies
  • Cache

Content-Length: 742

Content-Type: application/json
Content-Type: text/html

Content-Disposition: attachment; filename="cat.jpg"

Accept: /
Accept: image/*
Accept: text/html

Headers

Body

Le mécanisme principal de transfert de données en HTTP est le body des requêtes et réponses. Outre les fichiers bruts (images, zip, etc.), il est judicieux d'uniformiser le format de donnée utilisé.

  • JSON
  • XML
  • Protobuf
  • ...

Exemple RPC -> REST

# BUCKET LISTS
GET /lists

POST /list-add
voyages

POST /list-edit
voyages
destinations

POST /list-delete
nourriture

# ITEMS D'UNE LISTE
GET /lists-items?list=...
GET /lists-items
destinations

POST /lists-items?list=...
nouvel item
POST /lists-items
destinations=allemagne

POST /list-items-delete
_ITEM ID_
# BUCKET LISTS
GET /lists

POST /lists
{ name: _NAME_ }

PATCH /lists/_NAME_
{ name: _NEW NAME_ }

DELETE /lists/_NAME_

# ITEMS D'UNE LISTE
GET /lists/_LIST NAME_/items
? GET /lists/items?list=_LIST NAME_

POST /lists/_NAME_/items
{ item: ... }
? POST /lists/items?list=_LIST NAME_
{ item: ... }

DELETE /lists/_NAME_/items/_ID_
DELETE /items/_ID_

Et les réponses?

Sinatra

require "bundler/inline"

gemfile do
source "http://rubygems.org"

gem "sinatra-contrib"

gem "rackup"
gem "webrick"
end

require "sinatra/base"
require "sinatra/reloader"

class MySinatraApp < Sinatra::Base

configure :development do
register Sinatra::Reloader
end

#
# VERBE
#

get "/" do
return "Bonjour à tous!"
end

# Une route est identifiee par le VERBE + CHEMIN
# GET / est different de DELETE /
delete "/" do
return "Ceci est une action DELETE"
end

#
# Parametre de chemin
#
get "/greetings/admin" do
return "Bonjour cher admin!"
end

# ATTENTION, l'ordre de declaration des routes est important
# /greetings/admin
# /greetings/james
get "/greetings/:name" do
# On peut preciser un parametre dans le chemin

return "Bonjour à #{params["name"]}!"
end

#
# Réponse: Status et Headers
#

# An Array with three elements: [status (Integer), headers (Hash), response body (responds to #each)]
# An Array with two elements: [status (Integer), response body (responds to #each)]
# An object that responds to #each and passes nothing but strings to the given block
# A Integer representing the status code

get "/status" do
return 204
end

get "/status/body" do
return [200, "Code 200, Tout est OK!"]
end

get "/status/body/headers" do
data = { name: "James Hoffman", title: "Enseignant", age: 42 }
headers = {
"Content-Type"=> "application/json"
}

return [
200,
headers,
data.to_json
]
end

#
# Requête: Headers
#

post "/headers" do
# Les headers sont formatés selon les règles suivantes
# - Nom en MAJUSCULE
# - Les tirets - deviennent des barres de soulignement _
# - Préfix HTTP_
# Exemple:
# Accept devient HTTP_ACCEPT
# User-Agent devient HTTP_USER_AGENT
#
# EXCEPTIONS
# CONTENT_LENGTH, CONTENT_TYPE

# Les données complémentaires de la requête
# sont disponible dans le Hash request.env
# Plusieurs helpers simplifient l'accès
# https://www.rubydoc.info/github/rack/rack/Rack/Request/Helpers
return [
request.content_type,

request.user_agent,

request.accept?("application/json").to_s,

request.has_header?("CONTENT_TYPE").to_s,
request.get_header("CONTENT_TYPE"),

request.get_header("HTTP_MY_HEADER"),

request.env.to_s,
request.env["HTTP_MY_HEADER"],
].join("\n\n\n\n")
end

run! if app_file == $0
end

Faraday

require "bundler/inline"

gemfile do
source "http://rubygems.org"
gem "faraday"
end

require "faraday"

server = Faraday.new "http://localhost:4567"

def show(response)
puts response.status

puts "---"
puts response.headers
puts "---"

puts response.body

puts "\n\n"
end

show server.get("/")

show server.delete("/")

show server.get("/greetings/admin")

show server.get("/greetings/james")

show server.get("/status")

show server.get("/status/body")

show server.get("/status/body/headers")

headers = {
"Content-Type" => "custom/james",
"Accept" => "text/html",
"My-Header" => "ahoy!"
}
show server.post("/headers", nil, headers)

👉 Recipeasy