Skip to main content

Flask: Accessing Request Data

Flask: Accessing Request Data

Flask, a lightweight Python web framework, provides robust mechanisms for accessing request data through the request object. This is essential for handling user inputs, query parameters, form submissions, file uploads, and other HTTP request components in web applications. Accessing request data is particularly important in data-driven applications, such as dashboards or machine learning (ML) interfaces using Pandas, where user inputs drive dynamic content. This guide covers Flask accessing request data, including the request object, common use cases, integration with Jinja2 templates, best practices, and practical examples, with a focus on data-driven scenarios.


01. Overview of Request Data in Flask

Request data includes information sent by the client in an HTTP request, such as query parameters, form data, files, headers, and cookies. Flask’s request object, imported from the flask module, provides access to this data, enabling dynamic interaction between the client and server.

  • Purpose: Extract and process client inputs for rendering responses or performing actions.
  • Key Component: request object, which encapsulates HTTP request details.
  • Use Cases: Filtering Pandas DataFrames, processing ML model inputs, handling form submissions, or retrieving URL parameters.

1.1 The request Object

The request object is a global context variable available within a Flask route. Common attributes include:

Attribute Description Example
request.method The HTTP method (GET, POST, etc.). request.method == 'POST'
request.args Query parameters (GET). request.args.get('name')
request.form Form data (POST). request.form.get('username')
request.files Uploaded files (POST). request.files['file']
request.headers HTTP headers. request.headers.get('User-Agent')
request.cookies Client cookies. request.cookies.get('session')
request.json JSON payload (POST). request.json['key']

02. Accessing Query Parameters (GET Requests)

Query parameters are included in the URL (e.g., ?key=value) and accessed via request.args. They are commonly used for filtering or searching data.

Example: Filtering Data with Query Parameters

File: app.py

from flask import Flask, render_template, request
import pandas as pd

app = Flask(__name__)

@app.route('/search')
def search():
    name_filter = request.args.get('name', '').lower()
    df = pd.DataFrame({
        'Name': ['Alice', 'Bob', 'Charlie'],
        'Age': [25, 30, 35]
    })
    if name_filter:
        df = df[df['Name'].str.lower().str.contains(name_filter)]
    return render_template('search.html', data=df.to_dict(orient='records'))

if __name__ == '__main__':
    app.run(debug=True)

File: templates/search.html

<!DOCTYPE html>
<html>
<head>
    <title>Search</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <h1>Search Users</h1>
        <form method="get">
            <div class="form-group">
                <label>Name Filter:</label>
                <input type="text" name="name" class="form-control" value="{{ request.args.get('name', '') | escape }}">
            </div>
            <button type="submit" class="btn btn-primary">Search</button>
        </form>
        <table class="table table-striped mt-3">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Age</th>
                </tr>
            </thead>
            <tbody>
                {% for row in data %}
                    <tr>
                        <td>{{ row.Name | title }}</td>
                        <td>{{ row.Age }}</td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
</body>
</html>

Output (http://127.0.0.1:5000/search?name=al):

A Bootstrap-styled page with a form and a table showing only Alice.

Output (http://127.0.0.1:5000/search):

Shows all users (Alice, Bob, Charlie).

Explanation:

  • request.args.get('name', ''): Retrieves the name query parameter, defaulting to an empty string.
  • Pandas: Filters the DataFrame based on the query.
  • | escape: Safely renders the input in the form.

03. Accessing Form Data (POST Requests)

Form data is sent in the body of POST requests and accessed via request.form. This is useful for submitting user inputs, such as ML model parameters.

Example: Processing Form Data

File: app.py

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/predict', methods=['GET', 'POST'])
def predict():
    if request.method == 'POST':
        age = request.form.get('age', type=int)
        salary = request.form.get('salary', type=float)
        prediction = 0.5  # Mock ML prediction
        return render_template('result.html', age=age, salary=salary, prediction=prediction)
    return render_template('form.html')

if __name__ == '__main__':
    app.run(debug=True)

File: templates/form.html

<!DOCTYPE html>
<html>
<head>
    <title>Predict</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <h1>ML Prediction Form</h1>
        <form method="post">
            <div class="form-group">
                <label>Age:</label>
                <input type="number" name="age" class="form-control" required>
            </div>
            <div class="form-group">
                <label>Salary:</label>
                <input type="number" step="0.01" name="salary" class="form-control" required>
            </div>
            <button type="submit" class="btn btn-primary">Predict</button>
        </form>
    </div>
</body>
</html>

File: templates/result.html

<!DOCTYPE html>
<html>
<head>
    <title>Result</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <h1>Prediction Result</h1>
        <p>Age: {{ age | escape }}</p>
        <p>Salary: ${{ salary | float | round(2) }}</p>
        <p>Prediction: {{ prediction | float | round(2) }}</p>
        <a href="{{ url_for('predict') }}" class="btn btn-secondary">Try Again</a>
    </div>
</body>
</html>

Output (/predict):

  • GET: Displays a form for age and salary.
  • POST (age=25, salary=50000): Shows Age: 25, Salary: $50000.00, and a mock prediction.

Explanation:

  • request.form.get('age', type=int): Retrieves and converts form data to the specified type.
  • | escape: Safely renders user input.
  • | round(2): Formats numerical output.

04. Accessing File Uploads

File uploads, sent via POST requests with multipart/form-data, are accessed via request.files. This is useful for uploading datasets or ML model inputs.

Example: Uploading a CSV File

File: app.py

from flask import Flask, render_template, request
import pandas as pd
import os

app = Flask(__name__)
UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        if 'file' not in request.files:
            return render_template('upload.html', error='No file selected')
        file = request.files['file']
        if file.filename == '':
            return render_template('upload.html', error='No file selected')
        if file and file.filename.endswith('.csv'):
            file_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
            file.save(file_path)
            df = pd.read_csv(file_path)
            return render_template('upload.html', data=df.to_dict(orient='records'))
        return render_template('upload.html', error='Invalid file format')
    return render_template('upload.html')

if __name__ == '__main__':
    os.makedirs(UPLOAD_FOLDER, exist_ok=True)
    app.run(debug=True)

File: templates/upload.html

<!DOCTYPE html>
<html>
<head>
    <title>Upload CSV</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <h1>Upload CSV File</h1>
        {% if error %}
            <p class="text-danger">{{ error | escape }}</p>
        {% endif %}
        <form method="post" enctype="multipart/form-data">
            <div class="form-group">
                <label>CSV File:</label>
                <input type="file" name="file" accept=".csv" class="form-control">
            </div>
            <button type="submit" class="btn btn-primary">Upload</button>
        </form>
        {% if data %}
            <h2>Data Preview</h2>
            <table class="table table-striped mt-3">
                <thead>
                    <tr>
                        {% for key in data[0].keys() %}
                            <th>{{ key | title }}</th>
                        {% endfor %}
                    </tr>
                </thead>
                <tbody>
                    {% for row in data %}
                        <tr>
                            {% for value in row.values() %}
                                <td>{{ value | escape }}</td>
                            {% endfor %}
                        </tr>
                    {% endfor %}
                </tbody>
            </table>
        {% endif %}
    </div>
</body>
</html>

Output (/upload):

  • GET: Displays a file upload form.
  • POST (valid CSV): Saves the file, reads it with Pandas, and displays a table.
  • POST (invalid/no file): Shows an error message.

Explanation:

  • request.files['file']: Accesses the uploaded file.
  • enctype="multipart/form-data": Enables file uploads.
  • Pandas: Reads the CSV and renders it as a table.

05. Accessing JSON Data

JSON data, often sent in POST requests from APIs or JavaScript clients, is accessed via request.json.

Example: Handling JSON Data

File: app.py

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/api/data', methods=['POST'])
def data():
    if request.is_json:
        json_data = request.json
        name = json_data.get('name', 'Guest')
        value = json_data.get('value', 0)
        return render_template('json.html', name=name, value=value)
    return render_template('json.html', error='Invalid JSON')

if __name__ == '__main__':
    app.run(debug=True)

File: templates/json.html

<!DOCTYPE html>
<html>
<head>
    <title>JSON Data</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <h1>JSON Data</h1>
        {% if error %}
            <p class="text-danger">{{ error | escape }}</p>
        {% else %}
            <p>Name: {{ name | title | escape }}</p>
            <p>Value: {{ value | escape }}</p>
        {% endif %}
    </div>
</body>
</html>

Test with cURL:

curl -X POST -H "Content-Type: application/json" -d '{"name":"Alice","value":42}' http://127.0.0.1:5000/api/data

Output:

A page displaying Name: Alice and Value: 42.

Explanation:

  • request.is_json: Verifies the request contains JSON.
  • request.json: Accesses the JSON payload as a Python dictionary.

06. Accessing Headers and Cookies

Headers and cookies provide metadata about the request, such as user agents or session information.

Example: Headers and Cookies

File: app.py

from flask import Flask, render_template, request, make_response

app = Flask(__name__)

@app.route('/info')
def info():
    user_agent = request.headers.get('User-Agent', 'Unknown')
    session_cookie = request.cookies.get('session', 'None')
    response = make_response(render_template('info.html', user_agent=user_agent, session_cookie=session_cookie))
    response.set_cookie('session', 'example_session')
    return response

if __name__ == '__main__':
    app.run(debug=True)

File: templates/info.html

<!DOCTYPE html>
<html>
<head>
    <title>Request Info</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <h1>Request Info</h1>
        <p>User-Agent: {{ user_agent | escape }}</p>
        <p>Session Cookie: {{ session_cookie | escape }}</p>
    </div>
</body>
</html>

Output (/info):

Displays the browser’s User-Agent and the session cookie (initially None, then example_session on refresh).

Explanation:

  • request.headers.get('User-Agent'): Retrieves the client’s browser information.
  • request.cookies.get('session'): Accesses the session cookie.
  • response.set_cookie: Sets a cookie for subsequent requests.

07. Best Practices for Accessing Request Data

7.1 Recommended Practices

  • Use .get Methods: Use request.args.get, request.form.get, etc., with defaults to handle missing data.
  • Validate Inputs: Check and sanitize inputs to prevent errors or attacks (e.g., use type=int for numbers).
  • Secure Outputs: Use | escape or Jinja2’s default escaping for user inputs in templates.
  • Handle File Uploads Safely: Validate file types, sizes, and store files securely.
  • Use CSRF Protection: For POST forms, use Flask-WTF to prevent cross-site request forgery.
  • Check Request Type: Use request.is_json for JSON or request.method for conditional logic.

7.2 Security Considerations

Example: Insecure Request Handling

File: app.py

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/comment', methods=['POST'])
def comment():
    comment = request.form['comment']
    return render_template('comment.html', comment=comment)

if __name__ == '__main__':
    app.run(debug=True)

File: templates/comment.html

<!DOCTYPE html>
<html>
<head><title>Comment</title></head>
<body>
    <p>{{ comment | safe }}</p>  <!-- Insecure -->
</body>
</html>

Input: comment=<script>alert('XSS')</script>

Output: Executes the script (XSS vulnerability).

Correct:

<!DOCTYPE html>
<html>
<head><title>Comment</title></head>
<body>
    <p>{{ comment | escape }}</p>  <!-- Safe -->
</body>
</html>

Output: Displays <script>alert('XSS')</script> safely.

Explanation:

  • Insecure - | safe with user input enables XSS.
  • Correct - | escape prevents script execution.

7.3 Practices to Avoid

  • Avoid Direct Indexing: Don’t use request.form['key'] without checking, as it raises errors for missing keys.
  • Avoid Unvalidated Files: Always validate file types and sizes.
  • Avoid Unchecked JSON: Verify request.is_json before accessing request.json.

08. Conclusion

Accessing request data in Flask is a cornerstone of building interactive, data-driven web applications, enabling dynamic handling of user inputs, files, and metadata. Key takeaways:

  • Use request.args, request.form, request.files, request.json, etc., to access different types of request data.
  • Integrate with Pandas for filtering data or processing uploads in data-driven apps.
  • Support ML workflows by handling form inputs or JSON payloads for predictions.
  • Follow best practices like input validation, secure output rendering, and CSRF protection.

By mastering request data access, you can create robust, secure, and user-friendly Flask applications that effectively manage client-server interactions in dynamic contexts!

Comments