Emmett provides several instruments to help you dealing with requests in your application. Let's see them.
When a request comes from a client, Emmett binds useful informations about it within the request
object, which can be accessed just with an import:
from emmett import request
It contains useful information about the current processing request, in particular:
attribute | description |
---|---|
scheme | could be http or https |
path | full path of the request |
host | hostname of the request |
method | the request HTTP method |
now | a pendulum Datetime object created with request |
headers | the headers of the request |
cookies | the cookies passed with the request |
body | the request body (if available) |
client | the IP Address of the client doing the request (if available) |
Please keep in mind that the now
attribute will always use the UTC timezone, by default.
If you need to access the local time of the request you can use now_local
:
# request datetime in local machine timezone
request.now_local
Note: since
now
is a pendulum Datetime object, you can easily change the timezone using thein_timezone
method, likerequest.now.in_timezone('Europe/Berlin')
.
Now, let's see how to deal with request variables.
Emmett's request
object also provides three important attributes about the active request:
attribute | awaitable | description |
---|---|---|
query_params | no | contains the URL query parameters |
body_params | yes | contains parameters passed into the request body |
files | yes | contains files passed into the request body |
All three attributes are sdict
objects and they work in the same way, within the exception of requiring await
or not, and an example may help you understand their dynamic:
from emmett import App, request
app = App(__name__)
@app.route("/post/<int:id>")
async def post(id):
editor = request.query_params.editor
if editor == "markdown":
text = (await request.body_params).text
elif editor == "html":
# code
#..
Now, when a client calls the URL /post/123?editor=markdown, the editor
parameter will be mapped into request.query_params
and we can access its value simply calling the parameter name as an attribute.
When the URL doesn't contain the query parameter you're trying to look at, this will be None
, so it's completely safe to call it. It won't raise any exception.
Now, what happens when the client does a POST request with the following body on the URL /post/123?editor=markdown?
{
"text": "this is an example post",
"date": "2014-10-15"
}
Simple: the request
's params attributes will look like this:
>>> request.query_params
<sdict {'editor': 'markdown'}>
>>> await request.body_params
<sdict {'date': '2014-10-15', 'text': 'this is a sample post'}>
You can always access the variables you need.
Speaking of handling requests, you would like to perform specific actions on errors.
If we look at the given example for the parameters again, what happens when the user calls the URL without passing the editor
query parameter?
Maybe you want to redirect the client with a default parameter:
from emmett import redirect, url
@app.route("/post/<int:id>")
async def post(id):
editor = request.query_params.editor
if editor == "markdown":
# code
elif editor == "html":
# code
else:
redirect(url('post', id, params={'editor': 'markdown'}))
which means that, when the editor
var is missing, we force the user to markdown.
The redirect
function of Emmett accepts a string for the URL, and acts like an exception, interrupting the execution of your code.
Maybe, you prefer to show your 404 page:
from emmett import abort
@app.on_error(404)
async def not_found():
return app.render_template("404.html")
@app.route("/post/<int:id>")
async def post(id):
editor = request.query_params.editor
if editor == "markdown":
# code
elif editor == "html":
# code
else:
abort(404)
That's all it takes.
So you've just learned three handy aspects of Emmett:
redirect
and abort
allow you to stop the execution of your code;app.on_error()
;app.render_template()
to render a specific template without the presence of an exposed function or a specific context.New in version 2.1
Emmett supports serving requests following the HTTP/2 standard. In order to enable serving such requests the h11
protocol implementation should be enabled in develop
and serve
commands using the --http-protocol
option.
Moreover, in order for the browser to use HTTP/2 protocol, SSL should be enabled. SSL certificate and key should be passed to develop
and serve
commands using the relevant --ssl-certfile
and --ssl-keyfile
options.
Hint: a self-signed SSL certificate can be generated for development purposes using the
openssl
command, like:openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes
.
Server push allows for the server to send responses to the client before the client sends the request itself. This is useful when the server can predict what the client will request, thereby saving time at the possible cost of bandwidth if the prediction is wrong.
Emmett supports HTTP/2 server push promises thanks to the Request.push_promise
coroutine, which accepts as parameter the url of the static file to be sent. Here is an example:
from emmett import request, url
@app.route("/")
async def index():
await request.push_promise(url("static", "some_asset.js"))
return {}