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 a413 RequestEntityTooLargeerror.MAX_FORM_MEMORY_SIZE: Maximum size of any non-file form field in amultipart/form-databody (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
sessioninside the generator, you must access it at least once in the view function before returning theResponse. This ensures theVary: Cookieheader is correctly set before the headers are sent. - Session Modification: Modifying the
sessioninside a generator wrapped bystream_with_contextis ineffective. Headers (includingSet-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)