我有一个中间件,我想记录每个请求/响应.如何访问POST数据?

And*_*bin 20 django rest

我有这个中间件

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
Run Code Online (Sandbox Code Playgroud)

问题是process_response方法中的请求没有.POST,也没有.DATA和.body.我正在使用django-rest-framework,我的请求有Content-Type:application/json

请注意,如果我将记录放入process_request方法 - 它有.body和我需要的一切.但是,我在一个日志条目中需要请求和响应.

And*_*bin 10

这是我完成的完整解决方案

"""
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
Run Code Online (Sandbox Code Playgroud)

注意,这个

'tags': {
    'url': request.build_absolute_uri()
}
Run Code Online (Sandbox Code Playgroud)

将允许您在哨兵中按网址过滤.


sia*_*ach 5

安德烈的解决方案将打破并发请求.您需要将主体存储在请求范围内的某个位置并在中获取它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
Run Code Online (Sandbox Code Playgroud)


vwv*_*dya 5

上面的所有答案都有一个潜在的问题——传递给服务器的大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's 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
Run Code Online (Sandbox Code Playgroud)

Django框架仅在一种情况下直接访问主体。(来自框架)

elif self.META.get('CONTENT_TYPE', '').startswith('application/x-www-form-urlencoded'):
Run Code Online (Sandbox Code Playgroud)

如您所见,属性主体将整个请求读入内存。结果,您的服务器可能会崩溃。此外,它容易受到 DoS 攻击。在这种情况下,我建议使用 HttpRequest 类的另一种方法。(来自框架)

def readlines(self):
    return list(iter(self))
Run Code Online (Sandbox Code Playgroud)

所以,你不再需要这样做

def process_request(self, request):
    request._body_to_log = request.body
Run Code Online (Sandbox Code Playgroud)

你可以简单地做:

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
Run Code Online (Sandbox Code Playgroud)

编辑:这种带有 request.readlines() 的方法有问题。有时它不记录任何内容。