Server-side HTTP
API Creation
Create an API?
• Django: Powerful web framework with a lot of modules. Great to build a complete website.
• Flask: Small Framework to build simple website.
• Bottle: Similar to Flask, but even simpler. Perfect to build an API
Available libraries/frameworks in Python:
Create an API
The Bottle Framework (single file module, no dependencies)
• Routing: Requests to function-call mapping with support for clean and dynamic URLs.
• Templates: Fast and pythonic built-in template engine
• Utilities: Convenient access to form data, file
uploads, cookies, headers and other HTTP-related metadata.
• Server: Built-in HTTP development server and support for other WSGI capable HTTP server.
WSGI is the Web Server Gateway Interface
Create an API
from bottle import *
@route('/hello/<name>') def index(name):
return 'Hello '+name
run(host='localhost', port=8080)
Hello world example:
Create an API
127.0.0.1 - - [05/Nov/2020 09:14:16] "GET / HTTP/1.1" 404 720
127.0.0.1 - - [05/Nov/2020 09:14:17] "GET /favicon.ico HTTP/1.1" 404
742
Create API
127.0.0.1 - - [05/Nov/2020 09:19:44] "GET /hello/S%C3%A9bastien HTTP/
1.1" 200 16
Create an API
from bottle import Bottle, run app = Bottle()
@app.route('/hello/<name>') def index(name):
return 'Hello '+name
run(app, host='localhost', port=8080)
Hello world example:
Create an API
@route('/square/<id:int>') def square(id):
Filters: :int, :float, :path, :re
/square/1
@route('/open/<filepath:path>') def open(filepath):
/open/images/sun.png
filepath
@route('/slug/<idSlug:re:[0-9]+-.*>') def slug(idSlug):
/slug/51-bottle
Create an API
@route(‘/square/<id:int>')
HTTP Request Methods
accept GET method
@post('/images')
@route('/images', method='POST') or
Same thing with PUT and DELETE
Create an API
@route('/square/<id:int>'):
def square(id):
Generating the Content:
• return no content (False, empty string, ...)
return False
• return a string
return 'Hello'
return json.dumps(dictObject)
• return a file
return open('file.ext', 'rb')
return bottle.static_file('file.ext') # Optimized by bottle
• return an iterable
return ['line1\n', 'line2\n']
yield 'line1\n'
yield 'line2\n'
Create an API
from bottle import route, response
@route('/get/<id:int>'):
def get(id):
response.content_type = 'application/json; charset=utf-8' return json.dumps({'id':id, 'data': ...})
When returning a string (or a string iterable), it is automatically
encoded with respect to the given content-type, since only bytes are sent back to the client.
The Bottle Response Object:
Here, the server sends a byte array encoded in utf-8, that represents
an object formatted in JSON.
Create an API
import xml.etree.ElementTree as ET root = ET.Element('root')
child = ET.SubElement(root, "child") child.text = "I'am a child"
child.set('class', 'content') print(ET.tostring(root))
# prints <root><child class='content'>I'm a child</child></root>
Create an API
from bottle import route, abort
@route('/get/<id:int>'):
def get(id):
abort(401, "Sorry, access denied.")
Sending Error:
@route('/getImage/<id:int>'):
def getImage(id):
redirect("/get/"+id)
Redirect:
API Testing
Arithmetic API
from bottle import * from json import *
@route("/add/<i:int>/<j:int>")
def add(i,j):return dumps([i+j])
@route("/sub/<i:int>/<j:int>")
def sub(i,j):return dumps([i-j])
@route("/mul/<i:int>/<j:int>")
def mul(i,j):return dumps([i*j])
@route("/div/<i:int>/<j:int>")
def div(i,j):return dumps([i//j,i%j]) run(host='localhost', port=8080)
Arithmetic API Testing
API Tester API Server
GET /add/2/2
Arithmetic API Testing
API Tester API Server
GET /add/2/2
[4]
Arithmetic API Testing
from requests import *
server_ip = "127.0.0.1"
server_port = 8080
r1 = get(f"http://{server_ip}:{server_port}/add/4/5") print(r1.text)
r2 = get(f"http://{server_ip}:{server_port}/add/
7/14")
print(r2.text)
Arithmetic API Testing
from requests import *
server_ip = "127.0.0.1"
server_port = 8080
r1 = get(f"http://{server_ip}:{server_port}/add/4/5") print(r1.text)
r2 = get(f"http://{server_ip}:{server_port}/add/
7/14")
print(r2.text)
[9] [21]
Arithmetic API Testing
from requests import * from json import * import unittest
class TestAPIMethods(unittest.TestCase):
server_ip = "127.0.0.1"
server_port = 8080 def test_add(self):
r1 = get(f"http://{self.server_ip}:{self.server_port}/add/4/5") l = loads(r1.text)
self.assertEqual(l, [9])
r1 = get(f"http://{self.server_ip}:{self.server_port}/add/2/2") l = loads(r1.text)
self.assertEqual(l, [4]) if __name__ == '__main__':
unittest.main()
Arithmetic API Testing
from requests import * from json import * import unittest
class TestAPIMethods(unittest.TestCase):
server_ip = "127.0.0.1"
server_port = 8080 def test_add(self):
r1 = get(f"http://{self.server_ip}:{self.server_port}/add/4/5") l = loads(r1.text)
self.assertEqual(l, [9])
r1 = get(f"http://{self.server_ip}:{self.server_port}/add/2/2") l = loads(r1.text)
self.assertEqual(l, [4])
if __name__ == '__main__':
unittest.main()
. --- Ran 1 test in 0.008s
OK
Arithmetic API Testing
from requests import * from json import * import unittest
class TestAPIMethods(unittest.TestCase):
server_ip,server_port = « 127.0.0.1 », 8080
…
def test_sub(self):
r1 = get(f"http://{self.server_ip}:{self.server_port}/sub/4/5") l = loads(r1.text)
self.assertEqual(l, [1])
r1 = get(f"http://{self.server_ip}:{self.server_port}/sub/2/2") l = loads(r1.text)
self.assertEqual(l, [0]) if __name__ == '__main__':
unittest.main()
Arithmetic API Testing
from requests import * from json import * import unittest
class TestAPIMethods(unittest.TestCase):
server_ip,server_port = « 127.0.0.1 », 8080
…
def test_sub(self):
r1 = get(f"http://{self.server_ip}:{self.server_port}/sub/4/5") l = loads(r1.text)
self.assertEqual(l, [1])
r1 = get(f"http://{self.server_ip}:{self.server_port}/sub/2/2") l = loads(r1.text)
self.assertEqual(l, [0])
if __name__ == '__main__':
unittest.main()
.F
======================================================================
FAIL: test_sub (__main__.TestAPIMethods)
--- Traceback (most recent call last):
File "/Users/tixeuil/src/bottle/bottle_unittest_1.py", line 20, in test_sub self.assertEqual(l, [1])
AssertionError: Lists differ: [-1] != [1]
First differing element 0:
-1 1
- [-1]
? - + [1]
--- Ran 2 tests in 0.010s
FAILED (failures=1)
Arithmetic API Testing
from requests import * from json import * import unittest
class TestAPIMethods(unittest.TestCase):
server_ip,server_port = « 127.0.0.1 », 8080
…
def test_sub(self):
r1 = get(f"http://{self.server_ip}:{self.server_port}/sub/4/5") l = loads(r1.text)
self.assertEqual(l, [-1])
r1 = get(f"http://{self.server_ip}:{self.server_port}/sub/2/2") l = loads(r1.text)
self.assertEqual(l, [0]) if __name__ == '__main__':
unittest.main()
Arithmetic API Testing
from requests import * from json import * import unittest
class TestAPIMethods(unittest.TestCase):
server_ip,server_port = « 127.0.0.1 », 8080
…
def test_sub(self):
r1 = get(f"http://{self.server_ip}:{self.server_port}/sub/4/5") l = loads(r1.text)
self.assertEqual(l, [-1])
r1 = get(f"http://{self.server_ip}:{self.server_port}/sub/2/2") l = loads(r1.text)
self.assertEqual(l, [0])
if __name__ == '__main__':
unittest.main()
..
--- -
Ran 2 tests in 0.011s
OK
String API Testing
from bottle import * from json import *
@route("/to_upper/<s>") def to_upper(s):
return dumps(s.upper())
run(host='localhost', port=8080)
String API Testing
from bottle import * from json import *
@route("/to_upper/<s>") def to_upper(s):
return dumps(s.upper())
run(host='localhost', port=8080)
String API Testing
from requests import * from json import * import unittest
class TestAPIMethods(unittest.TestCase):
server_ip, server_port = ‘127.0.0.1’, 8080 def test_to_upper(self):
r1 = get(f"http://{self.server_ip}:{self.server_port}/to_upper/sébastien") l = loads(r1.text)
self.assertEqual(l, 'SÉBASTIEN')
r1 = get(f"http://{self.server_ip}:{self.server_port}/to_upper/tixeuil") l = loads(r1.text)
self.assertEqual(l, 'TIXEUIL') if __name__ == '__main__':
unittest.main()
String API Testing
from requests import * from json import * import unittest
class TestAPIMethods(unittest.TestCase):
server_ip, server_port = ‘127.0.0.1’, 8080 def test_to_upper(self):
r1 = get(f"http://{self.server_ip}:{self.server_port}/to_upper/sébastien") l = loads(r1.text)
self.assertEqual(l, 'SÉBASTIEN')
r1 = get(f"http://{self.server_ip}:{self.server_port}/to_upper/tixeuil") l = loads(r1.text)
self.assertEqual(l, 'TIXEUIL')
if __name__ == '__main__':
unittest.main()
.
--- -
Ran 1 test in 0.009s
OK
API Using
API User is a Bottle Web Server
from bottle import route, request, run from json import *
from requests import get
server_ip = "127.0.0.1"
server_port = 8080
@route("/input") def input():
return '''
<form action="/input" method="post">
i: <input name="i" type="text" />
j: <input name="j" type="text" />
<input value="Add" type="submit" />
</form>
'''
API User is a Bottle Web Server
from bottle import route, request, run from json import *
from requests import get
server_ip = "127.0.0.1"
server_port = 8080
@route("/input") def input():
return '''
<form action="/input" method="post">
i: <input name="i" type="text" />
j: <input name="j" type="text" />
<input value="Add" type="submit" />
</form>
'''
API User is a Bottle Web Server
from bottle import route, request, run from json import *
from requests import get
server_ip = "127.0.0.1"
server_port = 8080
@route("/input") def input():
return '''
<form action="/input" method="post">
i: <input name="i" type="text" />
j: <input name="j" type="text" />
<input value="Add" type="submit" />
</form>
'''
@route("/input", method='POST') def do_input():
i = request.forms['i']
j = request.forms['j']
r = get(f"http://{server_ip}:
{server_port}/add/{i}/{j}") l = loads(r.text)
return f"<h1>{i}+{j}={l[0]}</h1>"
run(host='localhost', port=8081)
API User is a Bottle Web Server
from bottle import route, request, run from json import *
from requests import get
server_ip = "127.0.0.1"
server_port = 8080
@route("/input") def input():
return '''
<form action="/input" method="post">
i: <input name="i" type="text" />
j: <input name="j" type="text" />
<input value="Add" type="submit" />
</form>
'''
@route("/input", method='POST') def do_input():
i = request.forms['i']
j = request.forms['j']
r = get(f"http://{server_ip}:
{server_port}/add/{i}/{j}") l = loads(r.text)
return f"<h1>{i}+{j}={l[0]}</h1>"
run(host='localhost', port=8081)
API User is a Bottle Web Server
Web Client Web Server
API User API Server
GET /input
API User is a Bottle Web Server
Web Client Web Server
API User API Server
GET /input
<form action="/input" method="post">
i: <input name="i" type="text" />
j: <input name="j" type="text" />
<input value="Add" type="submit" />
</form>
API User is a Bottle Web Server
Web Client Web Server
API User API Server
GET /input
<form action="/input" method="post">
i: <input name="i" type="text" />
j: <input name="j" type="text" />
<input value="Add" type="submit" />
</form>
POST /input, i=2, j=2
API User is a Bottle Web Server
Web Client Web Server
API User API Server
GET /input
<form action="/input" method="post">
i: <input name="i" type="text" />
j: <input name="j" type="text" />
<input value="Add" type="submit" />
</form>
POST /input, i=2, j=2
GET /add/2/2
API User is a Bottle Web Server
Web Client Web Server
API User API Server
GET /input
<form action="/input" method="post">
i: <input name="i" type="text" />
j: <input name="j" type="text" />
<input value="Add" type="submit" />
</form>
POST /input, i=2, j=2
GET /add/2/2
[4]
API User is a Bottle Web Server
Web Client Web Server
API User API Server
GET /input
<form action="/input" method="post">
i: <input name="i" type="text" />
j: <input name="j" type="text" />
<input value="Add" type="submit" />
</form>
POST /input, i=2, j=2
GET /add/2/2
[4]
<h1>2+2=4</h1>
API User is a
Bottle Web Server
from bottle import route, request, run from json import *
from requests import get
server_ip = "127.0.0.1"
server_port = 8080
@route("/input") def input():
return '''
<form action="/input" method="post">
Word: <input name="s" type="text" />
<input value="To Uppercase" type="submit" />
</form>
'''
API User is a
Bottle Web Server
from bottle import route, request, run from json import *
from requests import get
server_ip = "127.0.0.1"
server_port = 8080
@route("/input") def input():
return '''
<form action="/input" method="post">
Word: <input name="s" type="text" />
<input value="To Uppercase" type="submit" />
</form>
'''
API User is a
Bottle Web Server
from bottle import route, request, run from json import *
from requests import get
server_ip = "127.0.0.1"
server_port = 8080
@route("/input") def input():
return '''
<form action="/input" method="post">
Word: <input name="s" type="text" />
<input value="To Uppercase" type="submit" />
</form>
'''
@route("/input", method='POST') def do_input():
s = request.forms[’s’]
r = get(f"http://{server_ip}:
{server_port}/to_upper/{s}") l = loads(r.text)
return f"<h1>{s}.upper()={l}</h1>"
run(host='localhost', port=8081)
API User is a
Bottle Web Server
from bottle import route, request, run from json import *
from requests import get
server_ip = "127.0.0.1"
server_port = 8080
@route("/input") def input():
return '''
<form action="/input" method="post">
Word: <input name="s" type="text" />
<input value="To Uppercase" type="submit" />
</form>
'''
@route("/input", method='POST') def do_input():
s = request.forms[’s’]
r = get(f"http://{server_ip}:
{server_port}/to_upper/{s}") l = loads(r.text)
return f"<h1>{s}.upper()={l}</h1>"
run(host='localhost', port=8081)
API User is a
Bottle Web Server
from bottle import route, request, run from json import *
from requests import get
server_ip = "127.0.0.1"
server_port = 8080
@route("/input") def input():
return '''
<form action="/input" method="post">
Word: <input name="s" type="text" />
<input value="To Uppercase" type="submit" />
</form>
'''
@route("/input", method='POST') def do_input():
s = request.forms.getunicode('s') r = get(f"http://{server_ip}:
{server_port}/to_upper/{s}") l = loads(r.text)
return f"<h1>{s}.upper()={l}</h1>"
run(host='localhost', port=8081)
API User is a
Bottle Web Server
from bottle import route, request, run from json import *
from requests import get
server_ip = "127.0.0.1"
server_port = 8080
@route("/input") def input():
return '''
<form action="/input" method="post">
Word: <input name="s" type="text" />
<input value="To Uppercase" type="submit" />
</form>
'''