Skip to main content

The HttpRequest Object

The HttpRequest object is the central data structure in this codebase for representing an incoming web request. Defined in django/http/request.py, it encapsulates all metadata, parameters, and headers provided by the client, providing a unified interface for both WSGI and ASGI handlers.

Core Request Attributes

When a request enters the system, the HttpRequest class (or one of its subclasses like WSGIRequest) populates several key attributes:

  • path and path_info: Strings representing the requested path.
  • method: The HTTP method used (e.g., 'GET', 'POST').
  • GET and POST: Instances of QueryDict containing query string and form data.
  • COOKIES: A standard Python dictionary containing all cookies.
  • META: A dictionary containing all available HTTP headers and environment variables.
  • FILES: A MultiValueDict containing uploaded files.

Case-Insensitive Header Access

While raw headers are stored in the META dictionary (often prefixed with HTTP_), the HttpRequest.headers property provides a more convenient HttpHeaders interface.

The HttpHeaders class (a subclass of CaseInsensitiveMapping) allows for case-insensitive lookups and supports using underscores in place of hyphens.

# Accessing headers in a view
def my_view(request):
# These are all equivalent
user_agent = request.headers.get('User-Agent')
user_agent = request.headers.get('user-agent')
user_agent = request.headers.get('user_agent')

Internally, HttpHeaders handles the translation between standard header names and the HTTP_-prefixed keys found in the WSGI environment via the parse_header_name class method.

Handling Parameters with QueryDict

The GET and POST attributes are instances of QueryDict. This specialized subclass of MultiValueDict is designed to handle cases where a single key has multiple values (e.g., <select multiple>).

Multi-Value Access

Standard dictionary access (request.GET['key']) returns only the last value for a key. To retrieve all values, use getlist():

# URL: /search/?tag=python&tag=django
tags = request.GET.getlist('tag') # ['python', 'django']

Immutability

By default, QueryDict objects originating from an HttpRequest are immutable. Attempting to modify them directly will raise an AttributeError. To modify the data, you must create a mutable copy:

# Creating a mutable copy to add parameters
query_params = request.GET.copy()
query_params['new_param'] = 'value'

Content Negotiation

The HttpRequest class provides built-in support for content negotiation using the MediaType utility class. This allows views to determine what content types the client prefers based on the Accept header.

Preferred Types

You can check if a client accepts a specific type or find the best match from a list of options:

# Check if client accepts JSON
if request.accepts('application/json'):
return JsonResponse({'data': 'value'})

# Select the best match from available options
preferred = request.get_preferred_type(['text/html', 'application/json'])

The MediaType class handles the parsing of quality factors (q values) and specificity (e.g., text/* vs text/html) to determine precedence, as seen in the HttpRequest.accepted_types property.

Security and URI Management

Host Validation

The get_host() method retrieves the host of the request. It validates the host against the ALLOWED_HOSTS setting to prevent CSRF and cache poisoning attacks. If the host does not match any pattern in ALLOWED_HOSTS, a DisallowedHost exception is raised.

Absolute URIs

The build_absolute_uri() method is used to construct full URLs, which is particularly useful for redirects or generating links for emails. It automatically accounts for the current scheme (HTTP vs HTTPS) and host.

# Build an absolute URI for the current request
full_url = request.build_absolute_uri()

# Build an absolute URI for a specific path
login_url = request.build_absolute_uri('/accounts/login/')

Request Body and Streams

The raw body of the request can be accessed via the request.body property. However, there are critical constraints:

  1. Single Read: The body can only be read once. Accessing request.body after reading from the request's data stream (via read() or by accessing POST/FILES) will raise a RawPostDataException.
  2. Size Limits: The system enforces a maximum size for the request body via the DATA_UPLOAD_MAX_MEMORY_SIZE setting. If the Content-Length exceeds this limit, a RequestDataTooBig exception is raised during the first attempt to read the body.

For large uploads or streaming data, the HttpRequest object provides a file-like interface with read(), readline(), and iteration support, which interfaces directly with the underlying _stream.