Core Extensions¶
Microdot is a highly extensible web application framework. The extensions described in this section are maintained as part of the Microdot project in the same source code repository.
WebSocket Support¶
Compatibility |
CPython & MicroPython
|
Required Microdot source files |
|
Required external dependencies |
None
|
Examples |
The WebSocket extension gives the application the ability to handle WebSocket
requests. The with_websocket
decorator is used to mark a route handler as a WebSocket handler. Decorated
routes receive a WebSocket object as a second argument. The WebSocket object
provides send()
and receive()
asynchronous methods to send and receive
messages respectively.
Example:
@app.route('/echo')
@with_websocket
async def echo(request, ws):
while True:
message = await ws.receive()
await ws.send(message)
Server-Sent Events Support¶
Compatibility |
CPython & MicroPython
|
Required Microdot source files |
|
Required external dependencies |
None
|
Examples |
The Server-Sent Events (SSE) extension simplifies the creation of a streaming
endpoint that follows the SSE web standard. The with_sse
decorator is used to mark a route as an SSE handler. Decorated routes receive
an SSE object as second argument. The SSE object provides a send()
asynchronous method to send an event to the client.
Example:
@app.route('/events')
@with_sse
async def events(request, sse):
for i in range(10):
await asyncio.sleep(1)
await sse.send({'counter': i}) # unnamed event
await sse.send('end', event='comment') # named event
Note
The SSE protocol is unidirectional, so there is no receive()
method in
the SSE object. For bidirectional communication with the client, use the
WebSocket extension.
Rendering Templates¶
Many web applications use HTML templates for rendering content to clients. Microdot includes extensions to render templates with the utemplate package on CPython and MicroPython, and with Jinja only on CPython.
Using the uTemplate Engine¶
Compatibility |
CPython & MicroPython
|
Required Microdot source files |
|
Required external dependencies |
|
Examples |
The Template
class is used to load a
template. The argument is the template filename, relative to the templates
directory, which is templates by default.
The Template
object has a render()
method that renders the template to a string. This method receives any
arguments that are used by the template.
Example:
from microdot.utemplate import Template
@app.get('/')
async def index(req):
return Template('index.html').render()
The Template
object also has a generate()
method, which returns a generator instead of a string. The
render_async()
and
generate_async()
methods
are the asynchronous versions of these two methods.
The default location from where templates are loaded is the templates
subdirectory. This location can be changed with the
Template.initialize
class
method:
Template.initialize('my_templates')
By default templates are automatically compiled the first time they are rendered, or when their last modified timestamp is more recent than the compiledo file’s timestamp. This loading behavior can be changed by switching to a different template loader. For example, if the templates are pre-compiled, the timestamp check and compile steps can be removed by switching to the “compiled” template loader:
from utemplate import compiled
from microdot.utemplate import Template
Template.initialize(loader_class=compiled.Loader)
Consult the uTemplate documentation for additional information regarding template loaders.
Using the Jinja Engine¶
Compatibility |
CPython only
|
Required Microdot source files |
|
Required external dependencies |
|
Examples |
The Template
class is used to load a
template. The argument is the template filename, relative to the templates
directory, which is templates by default.
The Template
object has a render()
method that renders the template to a string. This method receives any
arguments that are used by the template.
Example:
from microdot.jinja import Template
@app.get('/')
async def index(req):
return Template('index.html').render()
The Template
object also has a generate()
method, which returns a generator instead of a string.
The default location from where templates are loaded is the templates
subdirectory. This location can be changed with the
Template.initialize
class method:
Template.initialize('my_templates')
The initialize()
method also accepts enable_async
argument, which
can be set to True
if asynchronous rendering of templates is desired. If
this option is enabled, then the
render_async()
and
generate_async()
methods
must be used.
Note
The Jinja extension is not compatible with MicroPython.
Maintaining Secure User Sessions¶
Compatibility |
CPython & MicroPython
|
Required Microdot source files |
|
Required external dependencies |
|
Examples |
The session extension provides a secure way for the application to maintain user sessions. The session data is stored as a signed cookie in the client’s browser, in JSON Web Token (JWT) format.
To work with user sessions, the application first must configure a secret key that will be used to sign the session cookies. It is very important that this key is kept secret, as its name implies. An attacker who is in possession of this key can generate valid user session cookies with any contents.
To initialize the session extension and configure the secret key, create a
Session
object:
Session(app, secret_key='top-secret')
The with_session
decorator is the
most convenient way to retrieve the session at the start of a request:
from microdot import Microdot, redirect
from microdot.session import Session, with_session
app = Microdot()
Session(app, secret_key='top-secret')
@app.route('/', methods=['GET', 'POST'])
@with_session
async def index(req, session):
username = session.get('username')
if req.method == 'POST':
username = req.form.get('username')
session['username'] = username
session.save()
return redirect('/')
if username is None:
return 'Not logged in'
else:
return 'Logged in as ' + username
@app.post('/logout')
@with_session
async def logout(req, session):
session.delete()
return redirect('/')
The save()
and
delete()
methods are used to update
and destroy the user session respectively.
Cross-Origin Resource Sharing (CORS)¶
Compatibility |
CPython & MicroPython
|
Required Microdot source files |
|
Required external dependencies |
None
|
Examples |
The CORS extension provides support for Cross-Origin Resource Sharing
(CORS). CORS is a
mechanism that allows web applications running on different origins to access
resources from each other. For example, a web application running on
https://example.com
can access resources from https://api.example.com
.
To enable CORS support, create an instance of the
CORS
class and configure the desired options.
Example:
from microdot import Microdot
from microdot.cors import CORS
app = Microdot()
cors = CORS(app, allowed_origins=['https://example.com'],
allow_credentials=True)
Testing with the Test Client¶
Compatibility |
CPython & MicroPython
|
Required Microdot source files |
|
Required external dependencies |
None
|
The Microdot Test Client is a utility class that can be used in tests to send requests into the application without having to start a web server.
Example:
from microdot import Microdot
from microdot.test_client import TestClient
app = Microdot()
@app.route('/')
def index(req):
return 'Hello, World!'
async def test_app():
client = TestClient(app)
response = await client.get('/')
assert response.text == 'Hello, World!'
See the documentation for the TestClient
class for more details.
Deploying on a Production Web Server¶
The Microdot
class creates its own simple web server. This is enough for an
application deployed with MicroPython, but when using CPython it may be useful
to use a separate, battle-tested web server. To address this need, Microdot
provides extensions that implement the ASGI and WSGI protocols.
Using an ASGI Web Server¶
Compatibility |
CPython only
|
Required Microdot source files |
|
Required external dependencies |
An ASGI web server, such as Uvicorn.
|
Examples |
The asgi
module provides an extended Microdot
class that
implements the ASGI protocol and can be used with a compliant ASGI server such
as Uvicorn.
To use an ASGI web server, the application must import the
Microdot
class from the asgi
module:
from microdot.asgi import Microdot
app = Microdot()
@app.route('/')
async def index(req):
return 'Hello, World!'
The app
application instance created from this class can be used as the
ASGI callable with any complaint ASGI web server. If the above example
application was stored in a file called test.py, then the following command
runs the web application using the Uvicorn web server:
uvicorn test:app
When using the ASGI support, the scope
dictionary provided by the web
server is available to request handlers as request.asgi_scope
.
Using a WSGI Web Server¶
Compatibility |
CPython only
|
Required Microdot source files |
|
Required external dependencies |
A WSGI web server, such as Gunicorn.
|
Examples |
The wsgi
module provides an extended Microdot
class that implements the
WSGI protocol and can be used with a compliant WSGI web server such as
Gunicorn or
uWSGI.
To use a WSGI web server, the application must import the
Microdot
class from the wsgi
module:
from microdot.wsgi import Microdot
app = Microdot()
@app.route('/')
def index(req):
return 'Hello, World!'
The app
application instance created from this class can be used as a WSGI
callbable with any complaint WSGI web server. If the above application
was stored in a file called test.py, then the following command runs the
web application using the Gunicorn web server:
gunicorn test:app
When using the WSGI support, the environ
dictionary provided by the web
server is available to request handlers as request.environ
.
Note
In spite of WSGI being a synchronous protocol, the Microdot application
internally runs under an asyncio event loop. For that reason, the
recommendation to prefer async def
handlers over def
still applies
under WSGI. Consult the Concurrency section for a discussion of how
the two types of functions are handled by Microdot.