У меня есть промежуточное программное обеспечение, в котором я хочу регистрировать каждый запрос/ответ. Как я могу получить доступ к данным 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")
.