django-rest-framework icon indicating copy to clipboard operation
django-rest-framework copied to clipboard

"No POST data" in error report

Open bblanchon opened this issue 4 years ago • 18 comments

Checklist

  • [X] Raised initially as discussion #7899
  • [X] This cannot be dealt with as a third party library. (We prefer new functionality to be in the form of third party libraries where possible.)
  • [X] I have reduced the issue to the simplest possible case.

Description

I'm using Django 3.1.7 & DRF 3.12.2. When an exception occurs in a ViewSet, Django automatically sends an email to the administrator.

The problem is that the POST data is always empty in this email:

POST: No POST data

Instead, I expect to see the JSON payload that was sent in the request body.

I added a breakpoint in django.utils.log.AdminEmailHandler.emit(), and I can confirm that request.POST is empty. But on the other hand, I'm sure that request.data was not empty in the ViewSet.

I verified that the request's Content-Type and X-Requested-With are correctly set. The only non-standard middlewares I use are corsheaders and admin_reorder; I don't think they are related to this problem.

bblanchon avatar Apr 27 '21 15:04 bblanchon

Any progress on this issue? I have the same problem.

danielmoessner avatar Jan 31 '22 12:01 danielmoessner

I can also confirm this issue, and it makes it a little bit hard to trace back errors

Here is a discussion related to this problem

Hafnernuss avatar Mar 01 '22 11:03 Hafnernuss

At this point I am convinced DRF is a dead project

rvinzent avatar Apr 08 '22 21:04 rvinzent

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jun 12 '22 16:06 stale[bot]

I still think this is an important feature, please don't close this issue.

bblanchon avatar Jun 12 '22 19:06 bblanchon

I agree.

Hafnernuss avatar Jun 12 '22 20:06 Hafnernuss

I'm also having this issue.

markdawson avatar Jul 29 '22 21:07 markdawson

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Oct 01 '22 02:10 stale[bot]

I still think this is an important feature, please don't close this issue.

bblanchon avatar Oct 01 '22 09:10 bblanchon

It is because django dose not parse body of application/json to request.POST field.You can convert json string to dict by yourself, like this: res = request.body json_str = res.decode('utf-8')
json_dict = json.loads(json_str)
Then write a custom log handler class instead of AdminEmailHandler to send your own dict data converted from json.

donghj2000 avatar Nov 14 '22 07:11 donghj2000

some code snippet:

 def emit(self, record):
        try:
            request = record.request
            subject = '%s (%s IP): %s' % (
                record.levelname,
                ('internal' if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS
                 else 'EXTERNAL'),
                record.getMessage()
            )

            import json
            from django.http import QueryDict
            json_str = request.body.decode('utf-8')
            json_dict = json.loads(json_str)
            query_dict = QueryDict(mutable=True)
            for key in json_dict.keys():
                query_dict[key] = json_dict[key]
            request.POST = query_dict
            
        except Exception as ex:
            subject = '%s: %s' % (
                record.levelname,
                record.getMessage()
            )
            request = None
        subject = self.format_subject(subject)

and the message is ...\n\nGET: No GET data\n\nPOST:\nusername = 'python1'\npassword = '123456'\n\nFILES: No FILES data...

donghj2000 avatar Nov 14 '22 11:11 donghj2000

Thanks, @donghj2000, this is awesome, but I still think this should be a built-in feature.

bblanchon avatar Nov 16 '22 15:11 bblanchon

Glad to help you. I have not found why they didn't do so. Another workaround is to use Middleware:

class ParseJsonData(MiddlewareMixin):
    def process_request(self, request):
        if request.content_type.startswith("application/json"):
            try:
                json_str = request.body.decode('utf-8')
                json_dict = json.loads(json_str)
                query_dict = QueryDict(mutable=True)
                for key in json_dict.keys():
                    query_dict[key] = json_dict[key]
                request.POST = query_dict
            except Exception as ex:
                pass

donghj2000 avatar Nov 17 '22 01:11 donghj2000

Glad to help you. I have not found why they didn't do so. Another workaround is to use Middleware:

class ParseJsonData(MiddlewareMixin):
    def process_request(self, request):
        if request.content_type.startswith("application/json"):
            try:
                json_str = request.body.decode('utf-8')
                json_dict = json.loads(json_str)
                query_dict = QueryDict(mutable=True)
                for key in json_dict.keys():
                    query_dict[key] = json_dict[key]
                request.POST = query_dict
            except Exception as ex:
                pass

This snippet has an issue where it assumes the JSON is an object and will fail on any other valid JSON value

rvinzent avatar Nov 17 '22 02:11 rvinzent

It seems to be a bug exactly, but request.POST is initialized as a QueryDict(django.http.request.py->class HttpRequest) ,so I don't know how to deal with other JSON type...

donghj2000 avatar Nov 17 '22 05:11 donghj2000

At this point I am convinced DRF is a dead project

no. it has 4 new more maintainers now. btw would love to see a failing test case to find out the fix for the problem. who is up for that? here is a comment pointing to the direction https://github.com/encode/django-rest-framework/pull/1671#issuecomment-62003465

auvipy avatar Nov 30 '22 02:11 auvipy