我有一个中间件,我想记录每个请求/响应.我如何访问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

注意,如果我把日志记录到process_request方法-它有.body和我需要的一切。但是,我需要在一个日志条目中同时包含请求和响应。

这是我做的完整解决方案

"""
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进行过滤。

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

以上所有答案都有一个潜在的问题——大请求。Body 传递给服务器。在Django请求中。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

Django框架只在一种情况下直接访问主体。(from framework)

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()的这种方法有问题。有时它不记录任何内容

令人沮丧和惊讶的是,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

这就像访问表单数据来创建一个新表单。

您必须使用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

你可以这样使用:


"""
Middleware to log requests and responses.
"""
import socket
import time
import json
import logging
request_logger = logging.getLogger(__name__)
class RequestLogMiddleware:
    """Request Logging Middleware."""
    def __init__(self, get_response):
        self.get_response = get_response
    def __call__(self, request):
        log_data = {}
        # add request payload to log_data
        req_body = json.loads(request.body.decode("utf-8")) if request.body else {}
        log_data["request_body"] = req_body
        # request passes on to controller
        response = self.get_response(request)
        # add response payload to our log_data
        if response and response["content-type"] == "application/json":
            response_body = json.loads(response.content.decode("utf-8"))
            log_data["response_body"] = response_body
        request_logger.info(msg=log_data)
        return response
    # Log unhandled exceptions as well
    def process_exception(self, request, exception):
        try:
            raise exception
        except Exception as e:
            request_logger.exception("Unhandled Exception: " + str(e))
        return exception

你也可以检查这个-日志请求通过中间件解释这个

还请注意,response.content返回字节字符串而不是unicode字符串,因此如果您需要打印unicode,则需要调用response.content.decode("utf-8")

您不能在中间件的process_response部分访问request.POST(或等价的request.body)。这张票提出了这个问题。虽然你可以在process_request部分拥有它。前面的答案给出了一个基于类的中间件。Django 2.0+和3.0+允许基于函数的中间件。

from .models import RequestData # Model that stores all the request data
def requestMiddleware(get_response):
    # One-time configuration and initialization.
    def middleware(request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.
        try : metadata = request.META ;
        except : metadata = 'no data'
        try : data = request.body ;
        except : data = 'no data'
        try : u = str(request.user)
        except : u = 'nouser'
        response = get_response(request)
        w = RequestData.objects.create(userdata=u, metadata=metadata,data=data )
        w.save()
        return response
    return middleware

模型RequestData如下-


class RequestData(models.Model):
    time = models.DateTimeField(auto_now_add=True)
    userdata = models.CharField(max_length=10000, default=' ')
    data = models.CharField(max_length=20000, default=' ')  
    metadata = models.CharField(max_length=20000, default=' ')  

最新更新