[python-nl] JSON API, CLI voorbeeld

Guido Wesdorp johnnydebris at gmail.com
Mon Apr 4 05:21:28 EDT 2016


Hoi Geert,

Aangezien REST niet een volledig protocol is, zul je denk ik geen generieke
client vinden. REST beschrijft in enigzins grove lijnen hoe je data en
acties op die data kunt ontsluiten door handig gebruik te maken van het
HTTP protocol, het beschrijft hoe je URLs zo in kunt delen dat ze netjes de
structuur van je data volgen en hoe je HTTP methods kunt gebruiken om
standaard acties op 'resources' (URL endpoints zeg maar) te definieren
(bijv. GET om te lezen, PUT om te schrijven, POST voor niet-standaard
acties, etc.), maar ook niet veel meer dan dat. Er wordt volgens mij niet
eens gedefinieerd wat voor Content-Type je response data heeft (hoewel
bijna iedereen JSON gebruikt, hoeft dat volgens mij niet per se) en heeft
bijvoorbeeld geen oplossing voor het aangeven van het type data dat je
ontsluit, je geeft met Content-Type aan dat je (bijv.) JSON verstuurt, maar
niet wat voor data-type die JSON representeert. REST is dus meer een stel
guide-lines dan een volledig protocol, voor bijv. WebDAV of JSON-RPC is het
wel mogelijk generieke clients te schrijven, maar REST is daar gewoon niet
compleet genoeg voor. Having said that, een specifieke REST client
schrijven is vaak erg eenvoudig met behulp van een generieke HTTP client
(library), aangezien REST zoals gezegd heel dicht bij HTTP probeert te
blijven.

In de standaard library van Python zitten 3 libraries voor het maken van
HTTP calls: urllib, urlllib2 en httplib. De eerste 2, 'urllib' en
'urllib2', zijn nagenoeg identiek (urllib2 is een opvolger van urllib, met
wat kleine verbeteringen) en helaas niet flexibel genoeg om goeie REST
clients te schrijven, voornamelijk omdat ze niet toestaan aan te geven
welke HTTP method er gebruikt moet worden bij een request (geef je geen
data mee wordt de GET method gebruikt, wanneer wel data wordt meegegeven
POST, PUT e.d. zijn niet mogelijk). De 'httplib' library is veel
flexibeler, maar ook een flink stuk moeilijker in gebruik. Eventueel kun
je, zoals voorgesteld, de 'requests' library gebruiken als gulden middenweg
(die staat wel toe de HTTP method te definieren, maar is een stuk
eenvoudiger in gebruik dan httplib), maar die zit niet in de standaard
library en zal dus moeten worden gedownload (pip install requests).

Om jouw puzzel compleet te maken, heb je nog een library nodig om JSON
parsing en generatie te doen en een library om command-line argumenten te
parsen. Voor het eerste heeft Python 'json' in de standaard library zitten,
voor het tweede zijn er vele opties, je kunt met 'sys.argv' zelf de
argumenten parsen of je kunt 'getopt' of 'argparse' of wat dan ook de
argument parser 'du jour' is (zoek ff op docs.python.org).

Ik snap dat dit allemaal misschien nog een beetje vaag is, dus ff snel een
voorbeeldje (vereenvoudigd, aangezien ik niet veel rekening houd met
errors, redirects etc. en geen argument parsing doe), waarin ik een JSON
structuur van de server afhaal met behulp van een HTTP GET request en
daarna weer opsla met PUT:

import httplib
import json

conn = httplib.HTTPConnection('localhost')
conn.request('GET', '/some/path')
res = conn.getresponse()
body = res.read()
if res.status not in 200:
  # note: should handle <= 400 as error, <= 300 as redirect
  raise Exception('something went wrong: %s %s - %s' % (res.status,
res.reason, body)
ctype = res.getheader('content-type')
if ctype.split(';')[0] != 'application/json':
  raise Exception('non-JSON response (%s)' % (ctype,))
data = json.loads(body)
print 'Received data on GET:', data

# adjust the data a bit and save it
data['foo'] = 'bar'
params = json.dumps(data).encode('UTF-8')
headers = {
  'Content-Type': 'application/json',
}
conn = httplib.HTTPConnection('localhost')
conn.request('PUT', '/some/path', params, headers)
res = conn.getresponse()
body = res.read()
if res.status not in 200:
  # note: should handle <= 400 as error, <= 300 as redirect
  raise Exception('something went wrong: %s %s - %s' % (res.status,
res.reason, body)
ctype = res.getheader('content-type')
if ctype.split(';')[0] != 'application/json':
  raise Exception('non-JSON response (%s)' % (ctype,))
data = json.loads(body)
print 'Received data on PUT:', data

Let op dat veel van de code bestaat uit omgaan met httplib, met 'requests'
zou het hele eerste stuk (regel 1 t/m 15) bijvoorbeeld kunnen worden
vervangen door:

import requests

res = requests.get('http://localhost/some/path')
print res.json()

dus het is misschien handig om ook even naar die lib te kijken. In ieder
geval, hou in gedachten dat je 'doodnormale' HTTP requests gaat uitvoeren,
waarin toevallig de response (en waarschijnlijk ook die van het request,
maar ook dat is optioneel) het MIME-type 'application/json' heeft.

Het parsen van command-line (de rest van je puzzel) is afhankelijk van de
library die je gebruikt, maar die worden prima beschreven in de Python docs.

Hoop dat dit een beetje helpt!

Groeten,

Guido

2016-04-03 16:00 GMT+02:00 Geert Stappers <stappers at stappers.nl>:

> Hoi,
>
> Mijn inschatting is dat er hier mensen zijn die
> weet hebben van Python code die tegen een JSON web API
> aan kan praten. Daar zoek wat command line voorbeelden van.
> Om voor een andere API een implementatie te maken.
>
> In gebruik doe je dan
>
>    command.py subcommand parameter parameter
>
> Dat er dat JSON de lijn over gaat
> en de user een statusberichtje krijgt.
>
> Mijn websearch op 'python json api cli client' brengt vooral
> subsets als "python json" of "python api".
>
> Wat in de buurt komt van wat ik zoek
> is https://github.com/jkbrzt/httpie
>
>
> Wat hebben jullie aan andere voorbeelden in die richting?
>
>
> Groeten
> Geert Stappers
> --
> Leven en laten leven
> _______________________________________________
> Python-nl mailing list
> Python-nl at python.org
> https://mail.python.org/mailman/listinfo/python-nl
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-nl/attachments/20160404/a449ffa4/attachment.html>


More information about the Python-nl mailing list