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.