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.