Skip to main content

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: A MultiDict containing parsed form data from POST or PUT requests.
  • args: A MultiDict containing query string parameters.
  • json: Parsed JSON data if the request's mimetype is application/json.
  • files: A MultiDict containing uploaded files.
  • url_rule: The werkzeug.routing.Rule that 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 a 413 RequestEntityTooLarge error.
  • MAX_FORM_MEMORY_SIZE: Limits the size of non-file form fields in multipart/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:

  1. Strings/Bytes: Becomes the response body with a 200 OK status.
  2. Dictionaries/Lists: Automatically converted to JSON using jsonify.
  3. Tuples: Can be (body, status), (body, headers), or (body, status, headers).
  4. 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 by as_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 to False shares a single instance across all requests, which is more efficient but requires using flask.g for request-specific data instead of self.

Request Dispatching

The internal process of matching a request to a view is handled by Flask.dispatch_request in src/flask/app.py.

  1. It checks if a routing_exception occurred during URL matching (e.g., a 404).
  2. It handles OPTIONS requests automatically if the rule allows it.
  3. It retrieves the view function from self.view_functions using the matched endpoint.
  4. It calls the view function with view_args extracted 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.