# Requests

## 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 every time 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.

{% hint style="success" %}
Read more about the IOC container in the [Service Container](https://docs.masoniteproject.com/v2.3/architectural-concepts/service-container) documentation.
{% endhint %}

## 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:

```python
def show(self, request: Request):
    request #== <masonite.request.Request>
```

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:

```python
def show(self, request: Request):
    request.input('username')
```

```python
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.

{% hint style="success" %}
Read more about helper functions in the [Helper Functions](https://docs.masoniteproject.com/v2.3/the-basics/helper-functions) documentation.
{% endhint %}

## 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:

```python
def show(self, request: Request):
    request.input('username')
```

{% hint style="info" %}
There is no difference between any HTTP methods (GET, POST, PUT, etc) when it comes to getting input data. They are all retrieved through this `.input()` method so there is no need to make a distinction if the request is `GET` or `POST`
{% endhint %}

### 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.

```python
# GET: /dashboard?user=Joe&status=1

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

This method will get all of the request input variables to include any internal framework variables completely handled internally such as \_\_token and \_\_method. You can exclude them by passing in False into the method or specifying it explicitly:

```python
# GET: /dashboard?user=Joe&status=1&__token=837674634

def show(self, request: Request):
    return request.all(internal_variables=False) # {'user': 'Joe', 'status': '1'}
```

To get a specific input:

```python
# GET: /dashboard?firstname=Joe

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

### Query Strings

Sometimes you want to only get the query strings or access the query strings. These can be times where you are posting to `POST: /dashboard?firstname=Joe` and want to access both the POST input AND the URL query strings.

You can do so simply:

```python
# POST: /dashboard?firstname=Joe

def show(self, request: Request):
    request.query('firstname') # Joe
    request.query('firstname', 'default') # Joe
    request.query('lastname', 'default') # default
```

There may be times you have multiple parameters you need to fetch. By default Masonite will only fetch the first one but you may have an example like this:

```python
# POST: /dashboard?firstname=Joe&firstname=Bob

def show(self, request: Request):
    request.query('firstname') # Joe
    request.query('firstname', multi=True) # ['Joe', 'Bob']
```

You may also get all the query params:

```python
# POST: /dashboard?firstname=Joe

def show(self, request: Request):
    return request.all_query() # {'firstname': 'Joe'}
```

### Input Cleaning

Input data will be cleaned of HTML tags and other security measures. This may cause unwanted return values if you are expecting something like a JSON string. If you want to opt to not clean the input you can specify that as a keyword argument:

```python
request.input('firstname', clean=False) # Joe
```

### Quotes

By default, Masonite will clean quotes in order to sanitize the input. If you would to preserve quotes you can set whether you would like quotes to be cleaned. A value of `False` will keep the quotes:

```python
request.input('firstname', quotes=False)
```

To check if some request input data exists:

```python
# GET: /dashboard?firstname=Joe

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

### Getting Dictionary Input

If your input is a dictionary you have two choices how you want to access the dictionary. You can either access it normally:

```python
request.input('payload')['user']['address'] # 123 Smith Rd
```

Or you can use dot notation to fetch the value for simplicity:

```python
request.input('payload.user.address') # 123 Smith Rd
```

You can also use a \* wildcard to get all values from a dictionary list. Take this code example:

```python
"""
Payload: 
"user": {
    "id": 1,
    "addresses": [
        {"id": 1, 'street': "A Street"},
        {"id": 2, 'street': "B Street"}
    ] 
}
"""

request.input('user.addresses.*.id') # [1,2]
```

You may also use list indexes to fetch data:

```python
"""
Payload: 
"user": {
    "id": 1,
    "addresses": [
        {"id": 1, 'street': "A Street"},
        {"id": 2, 'street': "B Street"}
    ] 
}
"""

request.input('user.addresses.2.street') # B Street
```

### Only

You can only get a certain set of parameters if you have a need to do so. This can be used like:

```python
# GET: /dashboard?firstname=Joe&lastname=Mancuso&active=1

def show(self, request: Request):
    return request.only('firstname', 'active') # {'firstname': 'Joe', 'active': '1'}
```

### Without

We can specify a set of parameters to exclude from the inputs returned. For example:

```python
# GET: /dashboard?firstname=Joe&lastname=Mancuso&active=1

def show(self, request: Request):
    return request.without('lastname') # {'firstname': 'Joe', 'active': '1'}
```

Notice it returned everything besides `lastname`.

### URL Parameters

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

```python
# Route: /dashboard/@firstname
# GET: /dashboard/Joe

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

### JSON Payloads

Sometimes you may want to handle incoming JSON requests. This could be form external API's like Github.

Masonite will detect that an incoming request is a JSON request and put the cast the JSON to a dictionary and load it into the payload request input. For example if you have an incoming request of:

```javascript
{
    "name": "Joe",
    "email": "Joe@email.com"
}
```

Then we can fetch this input in a controller using the normal `input()` method like so:

```python
def show(self, request: Request):
    request.input('name') # 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`.

{% hint style="warning" %}
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 although you can turn this off using a parameter.
{% endhint %}

### **Creating**

```python
def show(self, request: Request):
    return request.cookie('key', 'value')
```

### Not Encrypting

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:

```python
def show(self, request: Request):
    return request.cookie('key', 'value', encrypt=False)
```

### Expirations

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

```python
def show(self, request: Request):
    return request.cookie('key', 'value', expires="5 minutes")
```

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

### HttpOnly

Again, as a security measure, all cookies automatically are set with the `HttpOnly` flag which makes it unavailable to any Javascript code. You can turn this off:

```python
def show(self, request: Request):
    return request.cookie('key', 'value', http_only=False)
```

This will now allow Javascript to read the cookie.

### **Reading**

You can get all the cookies set from the browser

```python
def show(self, request: Request):
    return request.get_cookies()
```

You can get a specific cookie set from the browser

```python
def show(self, request: 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.:

```python
def show(self, request: Request):
    return request.get_cookie('key', decrypt=False)
```

This will return the plain text version of the cookie.

{% hint style="warning" %}
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.
{% endhint %}

### **Deleting**

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

```python
def show(self, request: 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.

```python
def show(self, request: Request):
    return request.user()
```

## Routes

You can also get a route URL via the route name. Let's say we have a route like this:

```python
Get('/dashboard').name('dashboard')
```

We can get the URL from the route name like so:

```python
def show(self):
    request().route('dashboard') # /dashboard
```

### Route Parsing

if we have route parameters like this:

```python
Get('/dashboard/@user').name('dashboard.user')
```

then we can pass in a dictionary:

```python
def show(self, request: Request):
    request.route('dashboard.user', {'user': 1}) # /dashboard/1
```

You may also pass a list if that makes more sense to you:

```python
def show(self, request: Request):
    request.route('dashboard.user', [1]) # /dashboard/1
```

This will inject that value for each parameter in order. For example if we have this route:

```python
Get('/dashboard/@user/@id/@slug').name('dashboard.user')
```

then we can use:

```python
def show(self, request: Request):
    request.route('dashboard.user', [1, 2, 'some-slug']) 
    # /dashboard/1/2/some-slug
```

### Contains

We can also check if a route contains a specific pattern:

```python
# GET /dashboard/user/1
def show(self, request: Request):
    request.contains('/dashboard/*/1') #== True
```

You can also use this in a template and pass in a `show` parameter to return a string instead. This is useful if you want to show active status classes depending on the current route:

```markup
<a href=".." class="{{ request().contains('/dashboard/*/edit', show='active')">
<a href=".." class="{{ request().contains('/dashboard/*/create', show='active')">
```

### Current URL

We can get the current url with:

```python
def show(self, request: Request):
    return request.path #== /dashboard/user
```

## Redirection

You can specify a url to redirect to

```python
def show(self, request: Request):
    return request.redirect('/home')
```

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

```python
def show(self, request: Request):
    return request.redirect('http://google.com')
```

You can redirect to a named route

```python
def show(self, request: Request):
    return request.redirect_to('dashboard')
```

You can also use the name parameter on the redirect method:

```python
def show(self, request: Request):
    return request.redirect(name="dashboard")
```

You can also redirect to a specific controller. This will find the URL that is attached to the controller method

```python
def show(self, request: Request):
    return request.redirect(controller="WelcomeController@show")
```

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`.

Redirecting to a named route with URL parameters:

```python
def show(self, request: Request):
    return request.redirect_to('dashboard', {'firstname': 'Joseph', 'lastname': 'Mancuso'})
```

Redirecting to a url in your application with URL parameters:

```python
def show(self, request: Request):
    return request.redirect('dashboard/@id', {'id': '1'})
```

### Form Back Redirection

Masonite will check for a `__back` input and redirect to that route. We can specify one using the `back()` view helper function:

```markup
<form action="{{ route('dashboard.create') }}" method="POST">
    {{ csrf_field }}
    {{ back() }}
</form>
```

By default the `back` helper will return the current path so you can easily go back to the previous page after the form is submitted.

You can also specify a path to go back to:

```markup
<form action="{{ route('dashboard.create') }}" method="POST">
    {{ csrf_field }}
    {{ back('/another/path') }}
</form>
```

### Redirecting Back

After submitting the form you can redirect back to wherever the `back` template method was pointing to using the `back()` method:

```python
def show(self, request: Request):
    ...
    return request.back()
```

This will also flash the current inputs to the session. You can then get the inputs using the `{{ old('key') }}` template helper:

```markup
<form>
  <input type="text" name="email" value="{{ old ('email') }}">
  ...
</form>
```

### Redirecting Back To Intended Routes

There are times where you want your user to visit a page where they must be logged in. You may have a check in your controller to redirect a user to the login page if they are not authenticated. You may easily redirect them back to the intended page after they login.

To activate the intended route you simply need to append `.then_back()` after the redirection. For example you may have this:

Assume the current URL is `/dashboard`

```python
from masonite.request import Request

class DashboardController:

    def show(self, request: Request):
        if not self.request.user():
            return request.redirect('/login').then_back()
```

When you go to your login page you'll need to have the normal `{{ back() }}` form helper:

```markup
<form action="{{ route('login') }}" method="POST">
    {{ csrf_field }}
    {{ back() }}
    ...
</form>
```

In your `LoginController` (or wherever this form submits to) you simply need to use the `redirect_intended` method on the request class:

```python
from masonite.request import Request

class LoginController(Controller):

    def store(self, request: Request)
        # ...
        if auth.login(request.input('email'), request.input('password')):
            return request.redirect_intended(default='/home')
```

This will redirect to the `/dashboard` route (because that was the intended route). If no route is intended then it will simply redirect to the default you specify (which is `/home`).

### Redirecting Back With Errors

You can redirect back with validation error message or redirect back with input value:

```python
def show(self, request: Request):
    errors = request.validate(
        required(['email', 'password'])
    )

    if errors:
        return request.back().with_errors(errors)
```

### Redirecting Back With Inputs

When redirecting back there are times where you will also want to flash the inputs to the session. With this you can simply use the `back()` method but if you want a bit more control you can use the `with_input()` method.

```python
def show(self, request: Request):
    errors = request.validate(
        required(['email', 'password'])
    )

    if errors:
        return request.redirect('/dashboard/errors').with_input()
```

You can then get the inputs using the `{{ old('key') }}` template helper:

```markup
<form>
  <input type="text" name="email" value="{{ old ('email') }}">
  ...
</form>
```

### Default Back URL

We can also specify a default route just in case a form submitted does not specify one using a form helper:

```python
def show(self, request: Request):
    return request.back(default='/hit/route')
```

This will check for the `__back` input and if it doesn't exist it will use this default route. This is the same as a redirect if you don't use the `back()` helper.

## Encryption Key

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

```python
request.key(key)
```

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

{% hint style="warning" %}
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.
{% endhint %}

## Headers

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

You can get all WSGI information by printing:

```python
def show(self, request: 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:

```python
def show(self, request: 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:

```python
def show(self, request: Request):
    request.header('AUTHORIZATION', 'Bearer some-secret-key')
```

{% hint style="warning" %}
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:
{% endhint %}

```python
request.header('AUTHORIZATION', 'Bearer some-secret-key')
```

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

You can also set headers with a dictionary:

```python
request.header({
    'AUTHORIZATION': 'Bearer some-secret-key',
    'Content-Type': 'application/json'
})
```

## 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:

```python
request.status('429 Too Many Requests')
```

You can also use an integer which will find the correct status code for you:

```python
request.status(429)
```

This snippet is exactly the same as the string based snippet above.

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.

## Get Request Method Type

You can get the request method simply:

```python
# PUT: /dashboard

def show(self, request: Request):
    return request.get_request_method() # 'PUT'
```

## Changing Request Methods in Forms and URLs

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:

```markup
<form action="/dashboard" method="POST">
    <input type="hidden" name="__method" value="PATCH">
</form>
```

or you can optionally use a helper method:

```markup
<form action="/dashboard" method="POST">
    {{ request_method('PATCH') }}
</form>
```

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

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

```python
from masonite.routes import Patch

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

You can also specify the request method in the query string of the url to change it on a link:

```markup
<a href="/dashboard?__method=PATCH" rel="nofollow">Dashboard patch</a>
```

This link will use the same route as above.

{% hint style="warning" %}
Changing the request method on a link from the default `GET` method should be done with caution. It can be useful while testing, but is not typically recommended. Adding `rel="nofollow"` may prevent search engines from following the link and causing data corruption.
{% endhint %}

## Request Information

You can get information related to the request like the scheme, domain and other attribute by accessing them right from the request class:

### Getting Domain Information

You can get different parts of the domain using the below methods:

```python
request.scheme() # https
request.host() # www.masonitecasts.com
request.port() # 8000
```

### Path Information

You can easily get the current path and query string information:

```python
request.full_path() # /dashboard/hello%20world
request.full_path(quoted=False) # /dashboard/hello world
request.query_string() # email=joe@masoniteproject.com
request.url() # www.masonitecasts.com/dashboard/hello%20world
request.url(include_standard_port=True) # www.masonitecasts.com:443/dashboard/hello%20world
request.full_url() # https://www.masonitecasts.com/dashboard/hello%20world
```

## Validation

There is a convenient helper method you can use the validate the request. You can import the `Validator` class and use validation like so:

```python
from masonite.request import Request
from masonite.validation import Validator

def show(self, request: Request, validate: Validator):
    errors = request.validate(
      validate.required('user')
    )

    if errors:
      return request.back().with_errors(errors)
```
