У меня есть промежуточное программное обеспечение, в котором я хочу регистрировать каждый запрос/ответ. Как я могу получить доступ к данным POST?

У меня есть это промежуточное ПО

import logging

request_logger = logging.getLogger('api.request.logger')


class LoggingMiddleware(object):

    def process_response(self, request, response):
        request_logger.log(logging.DEBUG,
               "GET: {}. POST: {} response code: {}. response "
               "content: {}".format(request.GET, request.DATA,
                                       response.status_code,
                                       response.content))
        return response

Проблема заключается в том, что запрос в методе process_response не имеет .POST, ни DATA, ни .body. Я использую django-rest-framework, и мои запросы имеют Content-Type: application/json

Обратите внимание, что если я помещаю logging в метод process_request - у него есть .body и все, что мне нужно. Тем не менее, мне нужны оба запроса и ответа в одной записи в журнале.

Ответы

Ответ 1

Вот полное решение, которое я сделал

"""
Api middleware module
"""
import logging

request_logger = logging.getLogger('api.request.logger')


class LoggingMiddleware(object):
    """
    Provides full logging of requests and responses
    """
    _initial_http_body = None

    def process_request(self, request):
        self._initial_http_body = request.body # this requires because for some reasons there is no way to access request.body in the 'process_response' method.


    def process_response(self, request, response):
        """
        Adding request and response logging
        """
        if request.path.startswith('/api/') and \
                (request.method == "POST" and
                         request.META.get('CONTENT_TYPE') == 'application/json'
                 or request.method == "GET"):
            request_logger.log(logging.DEBUG,
                               "GET: {}. body: {} response code: {}. "
                               "response "
                               "content: {}"
                               .format(request.GET, self._initial_http_body,
                                       response.status_code,
                                       response.content), extra={
                    'tags': {
                        'url': request.build_absolute_uri()
                    }
                })
        return response

Обратите внимание, что это

'tags': {
    'url': request.build_absolute_uri()
}

позволит вам фильтровать по url в часовом.

Ответ 2

Решение Andrey будет разбиваться на одновременные запросы. Вам нужно будет сохранить тело где-нибудь в области запроса и получить его в process_response().

class RequestLoggerMiddleware(object):

    def process_request(self, request):
        request._body_to_log = request.body

    def process_response(self, request, response):
        if not hasattr(request, '_body_to_log'):
            return response

        msg = "method=%s path=%s status=%s request.body=%s response.body=%s"
        args = (request.method,
                request.path,
                response.status_code,
                request._body_to_log,
                response.content)

        request_logger.info(msg, *args)

        return response

Ответ 3

Все приведенные выше ответы имеют одну потенциальную проблему - большой request.body, переданный серверу. В Django request.body - свойство. (из рамок)

@property
def body(self):
    if not hasattr(self, '_body'):
        if self._read_started:
            raise RawPostDataException("You cannot access body after reading from request data stream")
        try:
            self._body = self.read()
        except IOError as e:
            six.reraise(UnreadablePostError, UnreadablePostError(*e.args), sys.exc_info()[2])
        self._stream = BytesIO(self._body)
    return self._body

Django - только в одном случае. (из рамок)

elif self.META.get('CONTENT_TYPE', '').startswith('application/x-www-form-urlencoded'):

Как вы можете видеть, тело свойства считывает весь запрос в память. В результате ваш сервер может просто сбой. Более того, он становится уязвимым для DoS-атаки. В этом случае я бы предложил использовать другой метод класса HttpRequest. (из рамок)

def readlines(self):
    return list(iter(self))

Итак, вам больше не нужно это делать

def process_request(self, request):
    request._body_to_log = request.body

вы можете просто:

def process_response(self, request, response):

    msg = "method=%s path=%s status=%s request.body=%s response.body=%s"
    args = (request.method,
            request.path,
            response.status_code,
            request.readlines(),
            response.content)

    request_logger.info(msg, *args)

    return response

EDIT: этот подход с request.readlines() имеет проблемы. Иногда он ничего не записывает.

Ответ 4

Это похоже на доступ к данным формы для создания новой формы.

Вы должны использовать request.POST для этого (возможно, request.FILES - это то, что вы также регистрировали).

class LoggingMiddleware(object):

    def process_response(self, request, response):
        request_logger.log(logging.DEBUG,
               "GET: {}. POST: {} response code: {}. response "
               "content: {}".format(request.GET, request.POST,
                                       response.status_code,
                                       response.content))
        return response

Смотрите Здесь для свойств запроса.

Ответ 5

Это разочаровывает и удивительно, что в Django нет простого в использовании пакета ведения журнала запросов.

Итак, я создал его сам. Проверьте это: https://github.com/rhumbixsf/django-request-logging.git

Использует систему ведения журнала, поэтому ее легко настроить. Это то, что вы получаете с уровнем DEBUG:

GET/POST request url
POST BODY if any
GET/POST request url - response code
Response body

Ответ 6

Также обратите внимание, что response.content возвращает bytestring, а не строку unicode, поэтому, если вам нужно распечатать unicode, вам нужно вызвать response.content.decode("utf-8").