Django uses middleware to access the content in the request

The blogger mainly does a thing to record the request api log.

Disclaimer: This article specifies that the Django version is 1.8, and other versions are implemented online by yourself!

  1. Method 1: Nginx records requests and responses. I used Nginx to record the request information (already implemented, the recorded log is written on the file), but Nginx needs other Linux software to record the response information! If your machine doesn't have internet access, no yum etc, or I just hate manually installing software on Linux machines! please skip!
  2. In the method 2 project, someone proposed to use the decorator pattern to record logs on each view. First: Coupling, coupling, coupling! I think it is highly coupled! Although just import this method and add @funciton! Second: This decorator cannot record the response information! So please skip too!
  3. Method 3 uses middleware in Django (similar to the interceptor in serverlet in Java)!
    It is found that the request in the middleware and the request in the Django view are all from the django.core.handlers.wsgi.WSGIRequest object, and I feel great in an instant!
    It can record IP, user and other information but cannot access the body in the request. The reason is: you cannot access body after reading from request's data stream
    and then check the official website and find:
    Accessing request.POST or request.REQUEST inside middleware from process_request or process_view will prevent any view running after the middleware from being able to modify the upload handlers for the request, and should normally be avoided.
    Let's see where the error comes from:
    Django source code
 @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")

            # Limit the maximum request data size that will be handled in-memory.
            if (settings.DATA_UPLOAD_MAX_MEMORY_SIZE is not None and
                    int(self.META.get('CONTENT_LENGTH') or 0) > settings.DATA_UPLOAD_MAX_MEMORY_SIZE):
                raise RequestDataTooBig('Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.')

            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

Look at this meaning, don't read it, and then report an error! why?
Can't it work? wrong!
You can just copy this value to another variable:

re_request_body = getattr(request,'_body',request.body)
print re_request_body

You can't access request.body, but you can access the copied variable! Also, this method does not apply in the process_response method, I can in the process_request!
A netizen on Stack Overflow mentioned django restframework. I guess, restframework is the django.core.handlers.wsgi.WSGIRequest object that inherits Django. It can access body. I think it can also find other methods to directly access the variable reqeust.body Yes, but after reading the source code of restframework, I was confused, my ability was limited, and I couldn't understand it! Finally, I attach the record mentioned by foreign friends:
https://stackoverflow.com/questions/22740310/how-to-update-django-httprequest-body-in-middleware
write picture description here

Finally, I attach the code I can achieve:

import datetime
import random

class  middleware_record_req_rsp(object):

    def __init__(self):
        nowTime = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        randomNum = random.randint(0,1000)
        uniqueNum = str(nowTime) + str(randomNum)
        self.flag = uniqueNum

    def process_request(self,request):
        if request.method == 'POST':
            x_forward_for = request.META.get('HTTP_X_FORWARDED_FOR')
            request_ip = x_forward_for.split(',')[0] if x_forward_for else request.META.get('REMOTE_ADDR')
            request_user = request.META.get('USER')
            re_request_body = getattr(request,'_body',request.body)
            flag = self.flag
        return None

    def process_response(self,request,response):
        if request.method == 'POST':
            flag = self.flag
            response_status_code = response.status_code
        return response

Finally, I saw a good article introducing middleware!
http://blog.csdn.net/wolaiye320/article/details/52035451
ps: In comparison, browsing the information on the official website is the best. But if you can go to GitHub, Stack Overflow is the best place, especially Stack Overflow (the company's Internet Day recently

-------Dividing line-------

A very interesting question has arisen! I mentioned earlier that the request in the middleware process_response(request,response) cannot get the variable request.body, because the error "you cannot access body after reading from request's data stream" will be reported! I guess, the request in process_response(request,response) and process_request(request) are different!
As a result, I tested it:

  1. Pass the request in process_request to the initialization variable, and then compare the request variable in process_response(request,response),
    id(request) == id(self.request) request is self.re_quest and
    found to be the same. That's weird! Why can I get the body in the former, but not the value in the latter!

————Separation line————
So, in the response, can you modify the value passed to the front end in the response? The official website explains as follows:
write picture description here

it could alter the given response. Someone asked this on Stack Overflow: https://stackoverflow.com/questions/44112528/editing-response-contect-in-django-middleware

Inside is as follows:

Response is already rendered in the middleware stage so you can't just change response.data, you need to rerender it or change rendered content directly.

class RequestLogMiddleWare(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        if isinstance(response, Response):
            response.data['detail'] = 'I have been edited'
            # you need to change private attribute `_is_render` 
            # to call render second time
            response._is_rendered = False 
            response.render()
        return response
The second approach is just change content directly, but in that case built in rest framework browser API will not work because template will not render properly.

import json

class RequestLogMiddleWare(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        if isinstance(response, Response):
            response.data['detail'] = 'I have been edited'
            response.content = json.dumps(response.data)
        return response

Then I just change the value of content, and the change can be achieved!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325815299&siteId=291194637