Django 我有一个中间件,我想记录每个请求/响应。如何访问POST数据?
我有这个中间件Django 我有一个中间件,我想记录每个请求/响应。如何访问POST数据?,django,rest,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: {} respon
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
问题是进程中的请求\响应方法没有.POST或.DATA或.body。我使用的是django rest框架,我的请求的内容类型为:application/json
请注意,如果我将日志记录放在process_请求方法中,它有.body和我需要的所有内容。但是,我需要在单个日志条目中同时包含请求和响应。这就像访问表单数据来创建新表单一样 为此,您必须使用
request.POST
(也许request.FILES
也是您需要记录的内容)
请参阅以获取请求属性。这是我制定的完整解决方案
"""
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()
}
将允许您在sentry中按url进行筛选 令人沮丧和惊讶的是,Django中没有易于使用的请求日志记录包 所以我自己创造了一个。请查看: 使用日志系统,因此易于配置。这是调试级别的结果:
GET/POST request url
POST BODY if any
GET/POST request url - response code
Response body
Andrey的解决方案将在并发请求时中断。您需要将主体存储在请求范围中的某个位置,并在
process\u response()中获取它。
以上所有答案都有一个潜在的问题——传递给服务器的大request.body。在Django中,request.body是一个属性。(来自框架) Django框架仅在一种情况下直接访问主体。(来自框架) 如您所见,属性体将整个请求读取到内存中。因此,您的服务器可能会崩溃。此外,它还容易受到拒绝服务攻击。 在本例中,我建议使用HttpRequest类的另一个方法。(来自框架) 所以,你不再需要这样做了
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
编辑:使用request.readlines()的这种方法存在问题。有时它不会记录任何内容。还要注意,
response.content
返回的是testring而不是unicode字符串,因此如果需要打印unicode,则需要调用response.content.decode(“utf-8”)
您无法访问request.POST
(或等效的request.body
)在过程\u响应中
中间件的一部分。下面是一个提出问题的例子。尽管您可以在过程请求部分使用它。前面的答案给出了一个基于类的中间件。Django 2.0+和3.0+允许基于函数的中间件
from.models导入RequestData#存储所有请求数据的模型
def请求中间件(获取_响应):
#一次性配置和初始化。
def中间件(请求):
#之前为每个请求执行的代码
#该视图(以及更高版本的中间件)被称为。
try:metadata=request.META;
除外:元数据='无数据'
try:data=request.body;
除外:数据='无数据'
try:u=str(request.user)
除外:u='nouser'
响应=获取响应(请求)
w=RequestData.objects.create(userdata=u,metadata=metadata,data=data)
w、 保存()
返回响应
返回中间件
模型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=' ')
使用request.POSTinstead@LuisMasuelli不,它是空的。您可以分配request.OLD\u POST=request.POST
在process\u request
中,然后读取request.OLD\u POST
在process\u response
中,用您实施的解决方案写一个答案:p。有一天我或其他人会遇到这个问题。在这个解决方案中,任何请求都会覆盖\u initial\u http\u body,因此并发请求将引用同一个self。\u initial\u http\u body对象,属于稍后传入的请求的一个。当请求包含文件时会中断。我认为只有self.\u initial\u http\u body
在def process\u请求(self,request):
函数内就足够了。因为当您将\u initial\u http\u body=None
放入类中时,实际上是将此变量设置为类变量,而不是实例变量
,我认为这将破坏任何试图读取request.body的视图,出现以下错误:RawPostDataException:在读取请求的数据流后无法访问正文我对此表示怀疑。中间件没有在process\u请求中读取主体,因此如果它中断,则应该在view.post之后在process\u响应中。那不是真的吗?
elif self.META.get('CONTENT_TYPE', '').startswith('application/x-www-form-urlencoded'):
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
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=' ')