Skip to main content

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

asyncio_simple_http_server-0.0.8.tar.gz (21.9 kB view details)

Uploaded Source

Built Distribution

File details

Details for the file asyncio_simple_http_server-0.0.8.tar.gz.

File metadata

File hashes

Hashes for asyncio_simple_http_server-0.0.8.tar.gz
Algorithm Hash digest
SHA256 2db0bf70b47ccebff1893bb978c3727d3e9490520a1d1aaf163fdcac7d3fe009
MD5 b33e50927bd090aeb946eae725ee3df4
BLAKE2b-256 9a3a1c39c86778d15d1caafb1d25f651076f9c0ea365539d67ba49e4502fa0d3

See more details on using hashes here.

File details

Details for the file asyncio_simple_http_server-0.0.8-py3-none-any.whl.

File metadata

File hashes

Hashes for asyncio_simple_http_server-0.0.8-py3-none-any.whl
Algorithm Hash digest
SHA256 97f9a37639c357ab57d6832de5d735f3b52fe236e832e5189713d6b0413fdf52
MD5 5090e695fb4b31fdc119edb930e3cd75
BLAKE2b-256 07ec2d156fee8c3c57bcae26283a5b6a43cc08d633b0169f6099df5e8016ca5a

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page