A Simple Http API Server using asyncio
Project description
asyncio simple HTTP Server
This package contains a really Simple Http API Server using asyncio. It is not meant to be used in production. It is more a lightweight alternative to SimpleHTTPServer but for writing HTTP APIs.
Install the package
You can find the package at https://pypi.org/project/asyncio-simple-http-server.
Python >=3.8 is required. To install or upgrade you can use:
$ pip install --upgrade asyncio-simple-http-server
Usage Example
To start the server is the usual straightforward asyncio server code, plus some registration for your HTTP API handlers.
from asyncio_simple_http_server import HttpServer
import asyncio
async def main():
# Create an instance of the HTTP Server
http_server = HttpServer()
# Register one or more handlers
# The handlers are classes containing your APIs
# See below for an example and more information.
http_server.add_handler(MyHandler())
# If you need to enable CORS to call the APIs
# from a web console that you are building (e.g. angular, react, ...)
# just add the following line,
# to add the required header to all the responses.
http_server.add_default_response_headers({
'Access-Control-Allow-Origin': '*'
})
# start the server and serve/wait forever
await http_server.start('127.0.0.1', 8888)
await http_server.serve_forever()
if __name__ == '__main__':
asyncio.run(main())
An handler is a simple class where some methods can be exposed as an HTTP API, using a simple annotation. It also tries to simplify things for REST by converting the request body from json to a dict and the response object to json using the json.loads() and json.dumps() methods.
from asyncio_simple_http_server import uri_mapping
class MyHandler:
# The @uri_mapping decorator exposes this method as an HTTP API
# so you can just do a HTTP GET /test-get
# and you'll get back a 200 OK with a json body of {a: 10}
# return values will be converted with json.dumps()
@uri_mapping('/test-get')
def test_get(self):
return {'a': 10}
# In this case the mapping says that we want a POST method
# and by passing an argument named 'body' we will get the
# body passed to the endpoint parsed with json.loads()
# We execute "some logic" and we return the body
@uri_mapping('/test-post', method='POST')
def test_post(self, body):
foo = body.get('foo')
print('foo:', foo)
return body
Special Parameter Names
To receive the data of the HTTP request we use special parameter names, so if name a variable:
- request: will contains an HttpRequest object, which is the full request (method, path, header, body).
- headers: will contains an HttpHeaders object, which is a map of header name to values (a key can have multiple values).
- query_params will contains a dict[str, list[str]], which is a map of the query param name to list of values.
- raw_body: will contains the raw body as bytes.
- body: will contains the body converted from json using json.loads().
- uri_variables: will contains a dict of the variable specified in the @uri_variable_mapping and extracted from the path.
Custom HTTP responses (status code, headers, ...)
You can return an HttpResponse object to customize the response with your status code, headers and body encoding. You can also send a file by specifying the file path instead of the body.
from asyncio_simple_http_server import uri_mapping, HttpResponse, HttpHeaders
class MyHandler:
# You can return an HttpResponse object to set
# custom status code, headers and body
@uri_mapping('/test-custom-response')
def test_custom_resp(self) -> HttpResponse:
headers = HttpHeaders()
headers.set('X-Foo', 'custom stuff')
return HttpResponse(200, headers, b'test-body')
# With an HttpResponse object
# you can also send a file that is on disk
@uri_pattern_mapping('/send-my-file')
def test_file(self) -> HttpResponse:
return HttpResponse(200, file_path='/path/of/my-file')
Response Exceptions
You can also control the flow using HttpResponseException. If not catched by your code it will result in a response with the http status code, headers and body specified.
@uri_mapping('/test-exception')
def test_exception(self):
headers = HttpHeaders()
headers.set('X-Foo', 'custom stuff')
raise HttpResponseException(400, headers, b'custom-body')
Routes with patterns
Sometimes static routes are not enough. and you want a dynamic pattern. You can use @uri_variable_mapping and @uri_pattern_mapping to do exactly that.
from asyncio_simple_http_server import HttpRequest, uri_variable_mapping, uri_pattern_mapping
class MyHandler:
# You can also map routes with variables
# for example you can call this one with /aaa/FOO/ccc/BAR
# and you'll get {'bbb': 'FOO', 'ddd', 'BAR'} as uri_variables
@uri_variable_mapping('/aaa/{bbb}/ccc/{ddd}')
def test_due(self, uri_variables: dict):
return uri_variables
# Or you can use regex patterns to match what you want
# for example here you can call it as /any/FOO/BAR or /any/ZOO
@uri_pattern_mapping('/any/(.*)')
def test_pattern(self, request: HttpRequest):
return request.path
Oh, It uses asyncio
Oh yeah, I almost forgot. This server uses asyncio, so your function can be async too. just add async to the method and use all the awaits that you want.
class MyHandler:
@uri_mapping('/async-sleep')
async def test_async_sleep(self):
await asyncio.sleep(4)
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file asyncio_simple_http_server-0.0.8.tar.gz
.
File metadata
- Download URL: asyncio_simple_http_server-0.0.8.tar.gz
- Upload date:
- Size: 21.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 2db0bf70b47ccebff1893bb978c3727d3e9490520a1d1aaf163fdcac7d3fe009 |
|
MD5 | b33e50927bd090aeb946eae725ee3df4 |
|
BLAKE2b-256 | 9a3a1c39c86778d15d1caafb1d25f651076f9c0ea365539d67ba49e4502fa0d3 |
File details
Details for the file asyncio_simple_http_server-0.0.8-py3-none-any.whl
.
File metadata
- Download URL: asyncio_simple_http_server-0.0.8-py3-none-any.whl
- Upload date:
- Size: 19.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 97f9a37639c357ab57d6832de5d735f3b52fe236e832e5189713d6b0413fdf52 |
|
MD5 | 5090e695fb4b31fdc119edb930e3cd75 |
|
BLAKE2b-256 | 07ec2d156fee8c3c57bcae26283a5b6a43cc08d633b0169f6099df5e8016ca5a |