Requests

Introduction

The Request class is initialized when the server first starts and is modified on every request. This means that the Request class acts as a singleton and is not reinitialized on every request. This presents both pros and cons during developing Masonite. It's great to not have to worry about a new object being instantiated everytime but the con is that some attributes need to be reset at the end of the request.

The Request class is loaded into the IOC container first so any Service Provider will have access to it. The IOC container allows all parts of the framework to be resolved by the IOC container and auto inject any dependencies they need.

Read more about the IOC container in the Service Container documentation.

Getting Started

The Request class is bound into the IOC container once when the server is first started. This takes the WSGI environment variables generated by your WSGI server as a parameter. Because of this, we reload the WSGI values on every request but the actual Request object does not change. In other words, the memory address of the Request object is always the same but the class attributes will change of every request. This is done already for you by the Masonite framework itself. This Request class is bound and initialized inside the AppProvider Service Provider. We grab this request object by simply passing in Request into the parameters of anything resolved by the Service Container such as middleware, drivers and controller methods like so:

def show(self, Request):
    # Request is the instance of the Request class
    pass

Masonite is smart enough to know that we need the Request class and it will inject it into our method for us.

Helper Function

Masonite ships with a HelpersProvider Service Provider which adds several helper functions. One of these helper functions is the request() function. This function will return the request object. Because of this, these two pieces of code are identical:

app/http/controllers/YourController.py
def show(self, Request):
    Request.input('username')
app/http/controllers/YourController.py
def show(self):
    request().input('username')

Notice we didn't import anything at the top of our file and also didn't retrieve any objects from the IOC container. Masonite helper functions act just like any other built in Python function.

Read more about helper functions in the Helper Functions documentation.

Usage

The Request has several helper methods attached to it in order to interact with various aspects of the request.

In order to get the current request input variables such as the form data during a POST request or the query string during a GET request looks like:

app/http/controllers/YourController.py
def show(self, Request):
    Request.input('username')

There is no difference between any HTTP methods (GET, POST, PUT, etc) when it comes to getting input data. They are both retrieved through this .input() method so there is no need to make a distinction if the request is GET or POST

Method Options

Input Data

We can get all the request input variables such as input data from a form request or GET data from a query string. Note that it does not matter what HTTP method you are using, the input method will know what input data to get dependent on the current HTTP method (GET, POST, PUT, etc)

This will return all the available request input variables for that request as a dictionary.

app/http/controllers/YourController.py
# GET: /dashboard?user=Joe&status=1

def show(self, Request):
    return Request.all() # {'user': 'Joe', 'status': '1'}

To get a specific input:

app/http/controllers/YourController.py
# GET: /dashboard?firstname=Joe

def show(self, Request):
    return Request.input('firstname') # Joe

To check if some request input data exists:

app/http/controllers/YourController.py
# GET: /dashboard?firstname=Joe

def show(self, Request):
    return Request.has('firstname') # True

Incoming JSON Requests

All data can be retrieved as usual by using the input method above for incoming requests like form requests. Masnoite will handle application/json requests slightly differently. When the incoming request is JSON, masonite will load the JSON into a payload input which will return a dictionary representation of the JSON string.

app/http/controllers/YourController.py
def show(self, Request):
    return Request.input('payload') # returns {'id': 1, ...}

URL Parameters

To get the request parameter retrieved from the url. This is used to get variables inside: /dashboard/@firstname for example.

app/http/controllers/YourController.py
# GET: /dashboard/Joe

def show(self, Request):
    return Request.param('firstname') # Joe

Cookies

You may also set a cookie in the browser. The below code will set a cookie named key to the value of value.

By default, all cookies are encrypted with your secret key which is generated in your .env file when you installed Masonite. This is a security measure to ensure malicious Javascript code cannot fetch cookies if they are somehow retrieved. All cookies are set with the HTTP_ONLY flag meaning that Javascript cannot read them.

Creating

app/http/controllers/YourController.py
def show(self, Request):
    return Request.cookie('key', 'value')

If you choose to not encrypt your values and create cookies with the plain text value then you can pass a third value of True or False. You can also be more explicit if you like:

app/http/controllers/YourController.py
def show(self, Request):
    return Request.cookie('key', 'value', encrypt=False)

All cookies are set as session cookies. This means that when the user closes out the browser completely, all cookies will be deleted.

app/http/controllers/YourController.py
def show(self, Request):
    return Request.cookie('key', 'value', expires="5 minutes")

This will set a cookie thats expires 5 minutes from the current time.

Reading

You can get all the cookies set from the browser

app/http/controllers/YourController.py
def show(self, Request):
    return Request.get_cookies()

You can get a specific cookie set from the browser

app/http/controllers/YourController.py
def show(self, Request):
    return Request.get_cookie('key')

Again, all cookies are encrypted by default so if you set a cookie with encryption then this method will decrypt the cookie. If you set a cookie in plain text then you should pass the False as the second parameter here to tell Masonite not to decrypt your plain text cookie value.:

app/http/controllers/YourController.py
def show(self, Request):
    return Request.get_cookie('key', decrypt=False)

This will return the plain text version of the cookie.

If Masonite attempts to decrypt a cookie but cannot then Masonite will assume that the secret key that encrypted it was changed or the cookie has been tampered with and will delete the cookie completely.

If your secret key has been compromised then you may change the key at anytime and all cookies set on your server will be removed.

Deleting

You may also delete a cookie. This will remove it from the browser.

app/http/controllers/YourController.py
def show(self, Request):
    return Request.delete_cookie('key')

User

You can also get the current user from the request. This requires the LoadUserMiddleware middleware which is in Masonite by default. This will return an instance of the current user.

app/http/controllers/YourController.py
def show(self, Request):
    return Request.user()

Redirection

You can specify a url to redirect to

app/http/controllers/YourController.py
def show(self, Request):
    return Request.redirect('/home')

If the url contains http than the route will redirect to the external website

app/http/controllers/YourController.py
def show(self, Request):
    return Request.redirect('http://google.com')

You can redirect to a named route

app/http/controllers/YourController.py
def show(self, Request):
    return Request.redirectTo('dashboard')

You can also go back to a named route specified from the form input back. This will get the request input named back and redirect to that named route. This is great if you want to redirect the user to a login page and then back to where they came from. Just remember during your form submission that you need to supply a back input.

app/http/controllers/YourController.py
def show(self, Request):
    return Request.back()

This is equivalent to:

app/http/controllers/YourController.py
def show(self, Request):
    return Request.redirectTo(request.input('back'))

You can also specify the input parameter that contains the named route

app/http/controllers/YourController.py
def show(self, Request):
    return Request.back('redirect')

Sometimes your routes may require parameters passed to it such as redirecting to a route that has a url like: /url/@firstname:string/@lastname:string. For this you can use the send method. Currently this only works with named routes.

app/http/controllers/YourController.py
def show(self, Request):
    return Request.back().send({'firstname': 'Joseph', 'lastname': 'Mancuso'})

    return Request.redirectTo('dashboard').send({'firstname': 'Joseph', 'lastname': 'Mancuso'})

Encryption Key

You can load a specific secret key into the request by using:

Request.key(key)

This will load a secret key into the request which will be used for encryptions purposes throughout your Masonite project.

Note that by default, the secret key is pulled from your configuration file so you do NOT need to supply a secret key, but the option is there if you need to change it for testing and development purposes.

Headers

You can also get and set any headers that the request has.

You can get all WSGI information by printing:

app/http/controllers/YourController.py
def show(self, Request):
    print(Request.environ)

This will print the environment setup by the WSGI server. Use this for development purposes.

You can also get a specific header:

app/http/controllers/YourController.py
def show(self, Request):
    Request.header('AUTHORIZATION')

This will return whatever the HTTP_AUTHORIZATION header if one exists. If that does not exist then the AUTHORIZATION header will be returned. If that does not exist then None will be returned.

We can also set headers:

app/http/controllers/YourController.py
def show(self, Request):
    Request.header('AUTHORIZATION', 'Bearer some-secret-key')

Masonite will automatically prepend a HTTP_ to the header being set for standards purposes so this will set the HTTP_AUTHORIZATION header. If you do not want the HTTP prefix then pass a third parameter:

app/http/controllers/YourController.py
Request.header('AUTHORIZATION', 'Bearer some-secret-key', http_prefix=None)

This will set the AUTHORIZATION header instead of the HTTP_AUTHORIZATION header.

Status Codes

Masonite will set a status code of 404 Not Found at the beginning of every request. If the status code is not changed throughout the code, either through the developer or third party packages, as it passes through each Service Provider then the status code will continue to be 404 Not Found when the output is generated. You do not have to explicitly specify this as the framework itself handles status codes. If a route matches and your controller method is about to be hit then Masonite will set 200 OK and hit your route. This allows Masonite to specify a good status code but also allows you to change it again inside your controller method.

You could change this status code in either any of your controllers or even a third party package via a Service Provider.

For example, the Masonite Entry package sets certain status codes upon certain actions on an API endpoint. These can be 429 Too Many Requests or 201 Created. These status codes need to be set before the StartProvider is ran so if you have a third party package that sets status codes or headers, then they will need to be placed above this Service Provider in a project.

If you are not specifying status codes in a package and simple specifying them in a controller then you can do so freely without any caveats. You can set status codes like so:

Request.status('429 Too Many Requests')

This will set the correct status code before the output is sent to the browser. You can look up a list of HTTP status codes from an online resource and specify any you need to. There are no limitations to which ones you can use.

Changing Request Methods in Forms

Typically, forms only have support for GET and POST. You may want to change what HTTP method is used when submitting a form such as PATCH.

This will look like:

resources/templates/index.html
<form action="/dashboard" method="POST">
    <input type="hidden" name="request_method" value="PATCH">
</form>

or you can optionally use a helper method:

resources/templates/index.html
<form action="/dashboard" method="POST">
    {{ request_method('PATCH')|safe }}
</form>

When the form is submitted, it will process as a PUT request instead of a POST request.

This will allow this form to hit a route like this:

routes/web.py
from masonite.routes import Patch

ROUTES = [
    Patch().route('/dashboard', 'DashboardController@update')
]

Last updated