Note
Même dans le cas d'une application HTML "classique", on peut bénéficier d'une API grâce à AJAX.
JSON permet de représenter les données Python suivantes :
None
,{
'id': 1,
'label': "File",
'tooltip': None,
'items': [
{'label': "New", 'visible': True},
{'label': "Open", 'visible': True},
{'label': "Close", 'visible': False},
],
}
Très similaire à Python, à quelques exceptions près :
None
s'écrit null
,True
s'écrit true
,False
s'écrit false
,Note
Ces variantes proviennent principalement du langage Javascript, mais aussi d'une volonté de garder le langage JSON simple.
En terme de vocabulaire,
{
"id": 1,
"label": "File",
"tooltip": null,
"items": [
{"label": "New", "visible": true},
{"label": "Open", "visible": true},
{"label": "Close", "visible": false}
]
}
En Python standard :
import json
# avec des fichiers
data = json.load(open("file.json"))
json.dump(data, open("file2.json", "w"))
# avec des chaînes de caractères
txt = json.dumps(data)
data2 = json.loads(txt)
Dans Flask:
request.get_json()
fournit le contenu JSON de la requête (le cas échéant),
ou None
si le contenu est absent ou dans autre format.flask.jsonify(data)
produit un objet réponse dontcontent-type
est application/json
, etAvec HTML, on utilise exclusivement les verbes GET
et POST
,
mais HTTP définit d'autres verbes,
qui sont particulièrement utiles lors de la définition d'APIs.
Avertissement
Dans le TP précédent,
nous n'avons pas tout à fait respecté ces principes...
(exemple : quelle ressource identifie l'URL /Genes/del/<id>
?)
Note
L'acronyme REST et ses dérivés (comme "RESTful") a été inventé par Roy Fieldings dans sa thèse.
GET
pour obtenir la liste de ses éléments (avec leurs URLs)POST
pour créer un nouvel élémentGET
pour en obtenir une description détailléePUT
pour le modifierDELETE
pour le supprimerIl est possible d'implémenter différents verbes dans la même vue :
@app.route("/bidules/", methods=['GET', 'POST'])
def bidules():
if request.method == 'GET':
# ...
else:
# ...
Mais il est également possible de les implémenter dans différentes vues, (et donc d'avoir plusieurs routes ayant la même URL, mais des verbes différents) :
@app.route("/bidules/", methods=['GET'])
def bidules_list():
# ...
@app.route("/bidules/", methods=['POST'])
def bidules_new():
# ...
Le choix entre les deux options dépend de la quantité de code commun aux traitements des différents verbes.
Cache-control
§Voici deux valeurs typiques pour cache-control
dans une réponse :
max-age=T
(ou T est une durée en secondes) :
informe le client sur la durée pendant laquelle il est pertinent
de garder l'information en cache.no-cache
informe le client qu'il ne doit pas conserver cette réponse en cache,
pour le forcer à ré-interroger le serveur à la prochaine requête.ETag
§If-Match
: seulement si la ressource correspond à l'un des etags fournisIf-None-Match
: seulement si la ressource ne correspond à aucun des etags fournisL'objet Response
a une méthode set_etag
qui permet de lui associer un etag.
Note
Il ne faut pas utiliser resp.headers["etag"] = my_etag
car les etags doivent être encodés d'une manière spécifique.
L'objet request
a des attributs if_match
et if_none_match
qui contiennent la liste des etags fournis par le client
(correctement décodés).
Exemple d'utilisation :
@app.route('/my/url')
def smart_view():
etag = get_etag()
if etag in request.if_none_match:
resp = make_response("", 304) # 304 not modified
resp.set_etag(etag)
return resp
# here, make the "normal" response
Note
Flask offre une manière plus intégrée de gérer les requêtes GET conditionnelles :
@app.route('/my/url')
def smart_view():
# here, make the "normal" response
resp.set_etag(get_etag())
resp.make_conditional(request)
# transform the response according to conditional request
return resp
La réponse sera modifiée pour tenir compte de l'en-tête If-None-March
,
mais également de l'en-tête Range
(RFC 7233).
Cette solution est donc plus concise et plus puissante,
mais elle oblige à construire systématiquement la réponse,
ce qui peut avoir un coût significatif (accès à la base de données).
Le choix de l'une ou l'autre solution est donc une question de compromis.