aiohttp Server¶
Let’s start writing our own web application using aiohttp server.
Install aiohttp and Python 3.7 if you have not already. Using a virtual environment is recommended.
Example using venv
. In the command line:
python3.7 -m venv .venv
source .venv/bin/activate
(.venv) python3.7 -m pip install -U pip aiohttp
A simple web server example¶
First we will define a request handler, and it is a coroutine.
from aiohttp import web
async def handler(request):
return web.Response(text="Hello world")
The above is a coroutine that will return a Web Response, containing the text Hello
world
.
Once we have the request handler, create an Application instance:
app = web.Application()
app.add_routes([web.get('/', handler)])
In the above snippet, we’re creating an aiohttp web application, and registering
a URL route: /
(usually the root of the app), and we’re telling the app
to execute the handler
coroutine.
To run the app:
web.run_app(app)
Run the script, e.g. in the command line:
(.venv) python server.py
You should see the following output in the command line:
======== Running on http://0.0.0.0:8080 ========
(Press CTRL+C to quit)
You can now open your favorite web browser with the url http://0.0.0.0:8080. It should display “Hello world”.
The complete code can look like the following:
from aiohttp import web
async def handler(request: web.Request) -> web.Response:
return web.Response(text="Hello world")
async def init_app() -> web.Application:
app = web.Application()
app.add_routes([web.get("/", handler)])
return app
web.run_app(init_app())
Using route decorators¶
Notice in the previous example, we’re registering the root url by calling
app.add_routes
, and passing it a list of one URL. If you have more than one URL, it
is a matter of creating another request handler coroutine.
There is another way to define urls, by using decorators.
routes = web.RouteTableDef()
@routes.get('/')
async def handler(request):
return web.Response(text="Hello world")
app.add_routes(routes)
Both ways work, and it is a matter of your own personal choice.
URL Arguments and query parameters¶
When building web applications, sometimes you need to handle dynamic urls, instead of hardcoding.
Suppose we want to have urls for each users in our system, and greet the user,
by having the /{username}/
url.
@routes.get('/{username}')
async def greet_user(request: web.Request) -> web.Response:
user = request.match_info.get("username", "")
return web.Response(text=f"Hello, {user}")
Now re-start the server and open http://0.0.0.0:8080/<student> in your favorite browser.
Another way to parametrize resource is by using query parameters, for example
?page=1&uppercase=true
.
@routes.get('/{username}')
async def greet_user(request: web.Request) -> web.Response:
user = request.match_info.get("username", "")
page_num = request.rel_url.query.get("page", "")
return web.Response(text=f"Hello, {user} {page_num}")
Now try going to http://0.0.0.0:8080/<student>/?page=<pagenum>.
Serving other methods (POST, PUT, etc)¶
Notice so far we’ve been serving GET
resources. If you have resource that
needs to be accessed in other methods, like POST
or PUT
:
@routes.post('/add_user')
async def add_user(request: web.Request) -> web.Response:
data = await request.post()
username = data.get('username')
# Add the user
# ...
return web.Response(text=f"{username} was added")
Working with JSON¶
@routes.get('/json')
async def handler(request):
args = await request.json()
data = {'value': args['key']}
return web.json_response(data)
Full example for REST API¶
Example for simple blog REST API: Full REST server example
Post structure¶
Field |
Description |
Type |
---|---|---|
id |
Post id |
int |
title |
Title |
str |
text |
Content |
str |
owner |
Post creator |
str |
editor |
Last editor |
str |
API endpoints¶
- GET /api
List posts.
- POST /api
Add new post. Arguments: title, text, owner.
- GET /api/{post}
Fetch existing post. Return Post’s json.
- DELETE /api/{post}
Delete a post.
- PATCH /api/{post}
Edit a post. Arguments: title, text, editor.