Skip to main content

Request & Response Handling

Flask provides a robust infrastructure for handling HTTP requests and constructing responses by extending Werkzeug's base classes. The Request and Response objects are integrated with Flask's configuration system and context management to provide features like automatic JSON parsing, blueprint awareness, and streaming.

Extracting Request Data

The Request object (available as the request proxy) provides access to all incoming data. It extends werkzeug.wrappers.Request with Flask-specific attributes like endpoint, blueprint, and view_args.

from flask import Flask, request

app = Flask(__name__)

@app.route("/submit/<int:user_id>", methods=["POST"])
def handle_submission(user_id):
# URL parameters from the route
print(f"User ID: {user_id}")

# Query string parameters (?key=value)
search_term = request.args.get("q")

# Form data from POST/PUT requests
username = request.form.get("username")

# JSON data (automatically parsed if Content-Type is application/json)
data = request.get_json()

# Accessing the matched endpoint and blueprint
print(f"Endpoint: {request.endpoint}")
print(f"Blueprint: {request.blueprint}")

return "Data received"

Request Size Limits

Flask enforces limits on request data based on the application's configuration. These limits are checked by the Request object during data loading.

  • MAX_CONTENT_LENGTH: The maximum size of the request body. If exceeded, Flask raises a 413 RequestEntityTooLarge error.
  • MAX_FORM_MEMORY_SIZE: Maximum size of any non-file form field in a multipart/form-data body (default: 500,000 bytes).
  • MAX_FORM_PARTS: Maximum number of fields in a multipart body (default: 1,000).

You can set these in your app config:

app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024  # 16MB limit

Constructing Responses

Flask's make_response method converts various return types from view functions into a proper Response object.

Common Return Types

You can return strings, dictionaries, lists, or tuples directly from your view functions.

@app.route("/api/data")
def get_data():
# Returning a dict or list automatically calls jsonify()
return {"status": "ok", "items": [1, 2, 3]}

@app.route("/custom")
def custom_response():
# Returning a tuple: (body, status, headers)
return "Created", 201, {"X-Custom-Header": "Flask"}

Using make_response

If you need to modify the response object (e.g., to set a cookie) before returning it, use the make_response helper.

from flask import make_response, render_template

@app.route("/")
def index():
response = make_response(render_template("index.html"))
response.headers["X-Foo"] = "Bar"
response.set_cookie("visited", "true")
return response

Modifying Responses Per-Request

The after_this_request decorator allows you to register a function to run after the current request finishes. This is useful for modifying the response of a specific request without affecting others.

from flask import after_this_request

@app.route("/once")
def index():
@after_this_request
def add_header(response):
response.headers["X-Special"] = "One-time-header"
return response

return "Hello, World!"

Streaming Responses

For large responses, you can use a generator to stream data. The stream_with_context wrapper ensures that the AppContext (which holds request and session) remains active while the generator is yielding data.

from flask import Response, stream_with_context, request

@app.get("/stream")
def streamed_response():
@stream_with_context
def generate():
yield "Starting stream...\n"
# Accessing request data inside the generator
yield f"Query param: {request.args.get('q')}\n"
yield "Done."

return Response(generate(), mimetype="text/plain")

Streaming Gotchas

  • Session Access: If you need to access the session inside the generator, you must access it at least once in the view function before returning the Response. This ensures the Vary: Cookie header is correctly set before the headers are sent.
  • Session Modification: Modifying the session inside a generator wrapped by stream_with_context is ineffective. Headers (including Set-Cookie) are sent to the client before the generator starts yielding the body data.

Customizing Request and Response Classes

You can override the default Request and Response classes by setting request_class and response_class on your Flask application instance.

from flask import Flask, Request, Response

class CustomRequest(Request):
@property
def is_internal(self):
return self.remote_addr == "127.0.0.1"

class CustomResponse(Response):
default_mimetype = "application/json"

app = Flask(__name__)
app.request_class = CustomRequest
app.response_class = CustomResponse

Context Management

As of Flask 3.2, RequestContext has been merged into AppContext. The AppContext manages the lifecycle of a request and provides access to the request, session, and g objects. When a request starts, an AppContext is pushed, and it is popped after the response is sent and the request is finalized.

If you need to access request data outside of a view (e.g., in a CLI command or a background task), you can manually push an app context with environment data:

with app.app_context(environ=wsgi_environ):
print(request.path)