Request & Response Handling
Flask provides a robust system for handling HTTP requests and responses, extending the underlying Werkzeug library to integrate with Flask's application context and configuration system. This includes specialized wrapper classes, a flexible response-creation mechanism, and support for class-based views.
The Request Object
The Request object in Flask, defined in src/flask/wrappers.py, is a subclass of werkzeug.wrappers.Request. It adds Flask-specific metadata and integrates with the application's configuration.
Core Attributes and Proxies
In practice, developers interact with the current request via the request proxy. The Request class provides several key attributes for accessing incoming data:
form: AMultiDictcontaining parsed form data from POST or PUT requests.args: AMultiDictcontaining query string parameters.json: Parsed JSON data if the request's mimetype isapplication/json.files: AMultiDictcontaining uploaded files.url_rule: Thewerkzeug.routing.Rulethat matched the request.view_args: A dictionary of variables extracted from the URL (e.g.,{'id': 5}for a route/user/<id>).
Request Limits and Configuration
Flask enforces several limits on incoming requests based on the application's configuration. These are implemented as properties on the Request class in src/flask/wrappers.py:
MAX_CONTENT_LENGTH: The maximum size of the request body. If exceeded, Flask raises a413 RequestEntityTooLargeerror.MAX_FORM_MEMORY_SIZE: Limits the size of non-file form fields inmultipart/form-data.MAX_FORM_PARTS: Limits the number of fields in a multipart body to prevent denial-of-service attacks.
# src/flask/wrappers.py
@property
def max_content_length(self) -> int | None:
if self._max_content_length is not None:
return self._max_content_length
if not current_app:
return super().max_content_length
return current_app.config["MAX_CONTENT_LENGTH"]
The Response Object
The Response class, also in src/flask/wrappers.py, defaults to a text/html mimetype. While you can instantiate it directly, Flask typically handles response creation through the make_response method.
Flexible Return Types
Flask's make_response (found in src/flask/app.py) allows view functions to return a variety of types, which it automatically converts into a proper Response object:
- Strings/Bytes: Becomes the response body with a 200 OK status.
- Dictionaries/Lists: Automatically converted to JSON using
jsonify. - Tuples: Can be
(body, status),(body, headers), or(body, status, headers). - WSGI Callables: Any function that follows the WSGI specification.
# Example of tuple return from a view
@app.route("/api/data")
def get_data():
return {"key": "value"}, 201, {"X-Custom-Header": "Flask"}
The logic for unpacking these tuples is handled in Flask.make_response:
# src/flask/app.py
if isinstance(rv, tuple):
len_rv = len(rv)
if len_rv == 3:
rv, status, headers = rv
elif len_rv == 2:
if isinstance(rv[1], (Headers, dict, tuple, list)):
rv, headers = rv
else:
rv, status = rv
Class-Based Views
Flask provides class-based views in src/flask/views.py to allow for better code reuse and organization.
The View Base Class
The View class is the foundation for class-based views. To use it, you must override the dispatch_request method. You register it using the as_view class method, which converts the class into a view function.
class MyView(View):
def dispatch_request(self):
return "Hello from a class-based view!"
app.add_url_rule("/myview", view_func=MyView.as_view("my_view"))
MethodView
MethodView is a specialized subclass that dispatches requests to methods named after HTTP verbs (e.g., get(), post()). This is particularly useful for RESTful APIs.
# tests/test_views.py
class Index(flask.views.MethodView):
def get(self):
return "GET"
def post(self):
return "POST"
app.add_url_rule("/", view_func=Index.as_view("index"))
MethodView automatically populates the methods attribute by inspecting the class for defined HTTP verb methods. It also includes a "fallback" for HEAD requests: if head() is not defined but get() is, it will use get().
Advanced View Configuration
decorators: A list of decorators applied to the view function generated byas_view(). Note that decorating the class itself does not affect the resulting view function.init_every_request: By default, Flask creates a new instance of the view class for every request (True). Setting this toFalseshares a single instance across all requests, which is more efficient but requires usingflask.gfor request-specific data instead ofself.
Request Dispatching
The internal process of matching a request to a view is handled by Flask.dispatch_request in src/flask/app.py.
- It checks if a
routing_exceptionoccurred during URL matching (e.g., a 404). - It handles
OPTIONSrequests automatically if the rule allows it. - It retrieves the view function from
self.view_functionsusing the matched endpoint. - It calls the view function with
view_argsextracted from the URL.
# src/flask/app.py
def dispatch_request(self, ctx: AppContext) -> ft.ResponseReturnValue:
req = ctx.request
if req.routing_exception is not None:
self.raise_routing_exception(req)
rule: Rule = req.url_rule
if getattr(rule, "provide_automatic_options", False) and req.method == "OPTIONS":
return self.make_default_options_response(ctx)
view_args: dict[str, t.Any] = req.view_args
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
This dispatching logic ensures that whether you use a standard function-based view or a MethodView, the execution flow remains consistent and integrated with Flask's error handling and response finalization.