Процесс Django от запроса до ответа

задняя часть Django

Она была исправлена ​​30 января.Эта статья изначально была написана для перепечатки.Впоследствии было обнаружено, что в этой статье были некоторые логические ошибки.После ссылок на несколько других статей статья была обновлена ​​снова.Адреса нескольких справочных статей будет прикреплен в конце статьи.Если вы заинтересованы, читатели могут щелкнуть, чтобы увидеть его.

джанго начало

Когда мы запускаем проект django, независимо от того, запускаете ли вы его в командной строке или запускаете непосредственно в pycharm, он фактически выполняет операцию «runserver», а ruserver использует веб-сервер, поставляемый с django, который в основном используется для разработки и При отладке и в официальной среде обычно используется режим nginx+uwsgi.

В любом случае, при запуске проекта он делает 2 вещи:

  • Создайте экземпляр класса WSGIServer для приема запросов пользователей.
  • Когда поступает HTTP-запрос пользователя, укажите WSGIHandler, чтобы пользователь обрабатывал запрос и ответ пользователя.Этот обработчик является ядром обработки всего запроса.

WSGI

WSGI: Полное название — интерфейс шлюза веб-сервера.WSGI — это не сервер, не API для взаимодействия с программами и не код, а только определяет интерфейс для описания спецификации того, как веб-сервер взаимодействует с сетью. применение. Когда клиент отправляет запрос, веб-сервер, который сначала обрабатывает запрос, на самом деле является веб-сервером, таким как nginx и Apache, как мы часто говорим, а затем веб-сервер передает запрос веб-приложению (например, django) для обработки. Посредником посередине является WSGI, который соединяет веб-сервер с веб-фреймворком (Django).

这里写图片描述
Кратко представим часть содержимого WSGI, в нем оговаривается, что приложение является вызываемым объектом (функция/метод), а затем оно принимает 2 фиксированных параметра: один — переменная среды, содержащая серверную часть, а другой — вызываемый объект, который используется для инициализации ответа, добавляет к ответу код состояния и заголовок httpt и возвращает вызываемый объект. Вы можете увидеть простой пример

# 这段代码来自python核心编程
def simplr_wsgi_app(environ, start_response):
	# 固定两个参数,django中也使用同样的变量名
	status = '200 OK'
	headers = [{'Content-type': 'text/plain'}]
	# 初始化响应, 必须在返回前调用
	start_response(status, headers)
	# 返回可迭代对象
	return ['hello world!']

В django та же логика достигается с помощью класса WSGIHandler, на котором мы сосредоточимся ниже! Если вас интересуют WSGI и uWSGI, рекомендую всем прочитать эту статью,WSGI & uwsgi, здорово!

Основные понятия ПО промежуточного слоя

Как следует из названия, промежуточное ПО находится между веб-сервером и веб-приложением и может добавлять дополнительные функции. Когда мы создаем проект django (через pycharm), он автоматически устанавливает для нас необходимое промежуточное ПО.

MIDDLEWARE_CLASSES = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ПО промежуточного слоя либо предварительно обрабатывает данные, полученные от пользователя, и отправляет их приложению, либо выполняет некоторые окончательные корректировки полученных данных перед тем, как приложение вернет полезные данные ответа пользователю. Проще говоря, в django мидл может помочь нам подготовить объект запроса, а затем приложение может напрямую использовать объект запроса для получения различных данных, а также помочь нам добавить заголовок ответа, код состояния и т.д.

поток данных

Когда django получает запрос, он инициализирует WSGIHandler, что можно отследить в файле wsgi.py под проектом, и вы найдете этот класс.

class WSGIHandler(base.BaseHandler):
    def __call__(self, environ, start_response):
	    pass

Этот класс следует правилам приложения WSGI и принимает два параметра: один — это переменная среды, содержащая серверную часть, а другой — вызываемый объект, возвращающий итерируемый объект. Этот обработчик контролирует весь процесс от запроса до ответа, основной процесс:

handler

В интернете видел другую картинку, более полную:

handler2

大致几个步骤:
1. 用户通过浏览器请求一个页面  
2. 请求到达Request Middlewares,中间件对request做一些预处理或者直接response请求  
3. URLConf通过urls.py文件和请求的URL找到相应的View  
4. View Middlewares被访问,它同样可以对request做一些处理或者直接返回response  
5. 调用View中的函数  
6. View中的方法可以选择性的通过Models访问底层的数据  
7. 所有的Model-to-DB的交互都是通过manager完成的  
8. 如果需要,Views可以使用一个特殊的Context  
9. Context被传给Template用来生成页面  
    a.Template使用Filters和Tags去渲染输出  
    b.输出被返回到View  
    c.HTTPResponse被发送到Response Middlewares  
    d.任何Response Middlewares都可以丰富response或者返回一个完全不同的response  
    e.Response返回到浏览器,呈现给用户  

Порядок и методы в промежуточных классах

Классы промежуточного программного обеспечения Django содержат по крайней мере один из следующих четырех методов:
process_request, process_view, process_exception, process_response
WSGIHandler добавляет эти методы в четыре списка _request_middleware, _view_middleware, _response_middleware и _exception_middleware через load_middleware.
Не каждое промежуточное ПО имеет эти 4 метода.Если метод не существует, класс будет пропущен в процессе загрузки.

for middleware_path in settings.MIDDLEWARE_CLASSES:
	···
    if hasattr(mw_instance, 'process_request'):
        request_middleware.append(mw_instance.process_request)
    if hasattr(mw_instance, 'process_view'):
        self._view_middleware.append(mw_instance.process_view)
    if hasattr(mw_instance, 'process_template_response'):
        self._template_response_middleware.insert(0, mw_instance.process_template_response)
    if hasattr(mw_instance, 'process_response'):
        self._response_middleware.insert(0, mw_instance.process_response)
    if hasattr(mw_instance, 'process_exception'):
        self._exception_middleware.insert(0, mw_instance.process_exception)

Из исходного кода видно, что порядок выполнения и загрузки запроса процесса и ответа процесса обратный: в цикле process_request добавляется в конец списка, а process_request вставляется в начало.

middleware
(В некоторых случаях промежуточное программное обеспечение комментариев находится перед сеансом, и полезно знать порядок загрузки)

process_request

Приведите несколько примеров промежуточного программного обеспечения

class CommonMiddleware(object):
# 伪代码
    def process_request(self, request):

        # Check for denied User-Agents
        if 'HTTP_USER_AGENT' in request.META:
            for user_agent_regex in settings.DISALLOWED_USER_AGENTS:
                if user_agent_regex.search(request.META['HTTP_USER_AGENT']):
                    raise PermissionDenied('Forbidden user agent')
        host = request.get_host()

        if settings.PREPEND_WWW and host and not host.startswith('www.'):
            host = 'www.' + host
		pass

Process_request CommonMiddleware в основном предназначен для определения того, соответствует ли пользовательский агент требованиям, и для улучшения URL-адреса, например, для добавления www или добавления / в конце.

class SessionMiddleware(object):
    def process_request(self, request):
        session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
        request.session = self.SessionStore(session_key)

Process_request SessionMiddleware состоит в том, чтобы извлечь ключ session_key из файлов cookie и поместить его в request.session.

class AuthenticationMiddleware(MiddlewareMixin):
    def process_request(self, request):
        assert hasattr(request, 'session'), (
              "The Django authentication middleware requires session middleware "
              "to be installed. Edit your MIDDLEWARE%s setting to insert "
              "'django.contrib.sessions.middleware.SessionMiddleware' before "
              "'django.contrib.auth.middleware.AuthenticationMiddleware'."
        ) % ("_CLASSES" if settings.MIDDLEWARE is None else "")
        request.user = SimpleLazyObject(lambda: get_user(request))

Как упоминалось ранее, загрузка промежуточного программного обеспечения происходит в определенном порядке (в прямом и обратном порядке), Метод process_request AuthenticationMiddleware загружается на основе промежуточного программного обеспечения сеанса, а затем пользователь извлекается и помещается в request.user через сеанс запроса.

process_request должен возвращать None или объект HTTPResponse. Когда None возвращается, обработчик WSGI продолжит загрузку методов в process_request.В последнем случае обработчики будут напрямую загружать список _response_middleware, а затем отвечать напрямую.

парсинг URL

При обходе process_request в списке _request_middleware будет получен обработанный объект запроса (добавляются атрибуты request.session, request.user и другие).
Django будет выполнять регулярное сопоставление URL-адресов по порядку, и если совпадение не удастся, будет выдано исключение. Если промежуточное ПО запроса возвращает None, Django будет анализировать URL-адрес, запрошенный пользователем.
В настройке есть ROOT_URLCONF, который указывает на файл urls.py, по этому файлу можно сгенерировать urlconf, по сути это таблица сопоставления между url и функцией просмотра, а затем резолвер парсит URL-адрес пользователя для поиска первого совпадения.

process_view

После сопоставления URL будет получена функция просмотра и соответствующие параметры. Перед вызовом функции представления django загрузит каждый метод process_view в _view_middleware.
Я просмотрел промежуточное ПО по умолчанию один за другим и увидел только, что csrf имеет этот метод.

# 伪代码
class CsrfViewMiddleware(object):

    def process_view(self, request, callback, callback_args, callback_kwargs):

        if getattr(request, 'csrf_processing_done', False):
            return None

        try:
            csrf_token = _sanitize_token(
                request.COOKIES[settings.CSRF_COOKIE_NAME])
            # Use same token next time
            request.META['CSRF_COOKIE'] = csrf_token
        except KeyError:
            csrf_token = None
        if getattr(callback, 'csrf_exempt', False):
            return None
        pass

Функция этого метода заключается в том, чтобы определить, есть ли в cookie-файлах поле csrf. Если оно не существует, будет выброшено исключение напрямую. Если оно существует, оно вернет None. Промежуточное ПО представления такое же, как промежуточное ПО запроса. Оно должно возвращать None или httpResponse. Если возвращается httpresponse, то обработчики напрямую загружают список _response_middleware, а затем возвращают HttpResponse, после чего обработчики напрямую загружают список _response_middleware, а потом сразу отвечай

Выполнение логики просмотра

Функция просмотра должна удовлетворять:

  1. Представления на основе функций (FBV) или на основе классов (CVB).
  2. Первым принятым параметром должен быть запрос, и он должен возвращать объект ответа.

Если функция представления выдает исключение, обработчик будет проходить по списку _exception_middleware.Если возникнет исключение, последующее process_exception не будет выполнено.

process_response

На этом этапе мы получаем объект HTTPResponse, который может быть возвращен process_view или функцией просмотра. Теперь мы пройдемся по промежуточному программному обеспечению ответа. Это последний шанс промежуточного программного обеспечения скорректировать данные. Например:

class XFrameOptionsMiddleware(object):

    def process_response(self, request, response):
        # Don't set it if it's already in the response
        if response.get('X-Frame-Options') is not None:
            return response

        # Don't set it if they used @xframe_options_exempt
        if getattr(response, 'xframe_options_exempt', False):
            return response

        response['X-Frame-Options'] = self.get_xframe_options_value(request,
                                                                    response)
        return response

XFrameOptionsMiddleware добавляет X-Frame-Options к ответу, чтобы предотвратить вложение и захват веб-сайтов.

class CsrfViewMiddleware(object):
    def process_response(self, request, response):
        if getattr(response, 'csrf_processing_done', False):
            return response

        if not request.META.get("CSRF_COOKIE_USED", False):
            return response

        # Set the CSRF cookie even if it's already set, so we renew
        # the expiry timer.
        response.set_cookie(settings.CSRF_COOKIE_NAME,
                            request.META["CSRF_COOKIE"],
                            max_age=settings.CSRF_COOKIE_AGE,
                            domain=settings.CSRF_COOKIE_DOMAIN,
                            path=settings.CSRF_COOKIE_PATH,
                            secure=settings.CSRF_COOKIE_SECURE,
                            httponly=settings.CSRF_COOKIE_HTTPONLY
                            )
        # Content varies with the CSRF cookie, so set the Vary header.
        patch_vary_headers(response, ('Cookie',))
        response.csrf_processing_done = True
        return response

CsrfViewMiddleware устанавливает файлы cookie csrf в ответ

Наконец

Когда промежуточное программное обеспечение ответа загружено, система вызовет объект метода start_response, переданный с сервера WSGI, перед возвратом, инициализирует ответ, а затем ответит ответом.

Суммировать

В этой статье основное внимание уделяется:

  1. Когда django запускается, он запускает WSGIserver и генерирует обработчик для каждого запрошенного пользователя.
  2. Поймите протокол WSGI, и класс WSGIHandler управляет всем процессом запроса-ответа, а также основным процессом всего процесса.
  3. Концепция промежуточного программного обеспечения и роль каждого метода process_request, process_response, process_view, process_exception на каком этапе.
  4. Средняя цена выполняется по порядку, запрос и представление выполняются по порядку, а ответ и исключение выполняются в обратном порядке Этот шаг фактически завершается, когда WSGIHandler загружается в его различные списки.

Справочный блог:
  1. Примечания к учебнику по Django: 6. промежуточное ПО
  2. Чтобы заниматься веб-разработкой на Python, вам нужно понимать: WSGI и uwsgi.
  3. Какую обработку делал django от запроса до ответа  
  4. Что испытал Джанго из-за просьбы о возврате?