python - Django:捕获 RequestDataTooBig 异常

标签 python django error-handling

有没有办法在 Django 中捕获 RequestDataTooBig,中断执行并返回自定义错误?我应该在哪里做正确?

我有一个文本区域,用户可以输入任意多的文本,然后他按下发送按钮,django 失败并出现 RequestDataTooBig 异常(我测试了 5 MB 的文本)。

django.core.exceptions.RequestDataTooBig: Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.

我不想禁用此设置,我想捕获此异常并返回错误的 JSON。

当我试图在我的视野中捕获它时,像这样:

@require_http_methods([ "POST"])
def some_view(request):
    try:
        # some logic
        return JsonResponse({'key':'val'})
    except RequestDataTooBig:
        return JsonResponse({'error':'too_big_data'})

我反复收到此错误 - 必须有 block 。

django.core.exceptions.RequestDataTooBig: Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
...
django.core.exceptions.RequestDataTooBig: Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.

我怀疑我应该早点在某个地方捕获它 - 但不知道在哪里。

我用的是python3.4和django 1.10.5

那是回溯——我的 View 甚至没有被调用,异常似乎更深

Starting development server at http://0:80/
Quit the server with CONTROL-C.
middleware init
middleware init
middleware call
Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.
Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.
Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.
Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.
Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.
Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.
Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.
Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.
Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.
Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.
Traceback (most recent call last):
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/core/handlers/exception.py", line 39, in inner
    response = get_response(request)
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/core/handlers/base.py", line 178, in _get_response
    response = middleware_method(request, callback, callback_args, callback_kwargs)
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/middleware/csrf.py", line 260, in process_view
    request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/core/handlers/wsgi.py", line 128, in _get_post
    self._load_post_and_files()
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/http/request.py", line 311, in _load_post_and_files
    self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/http/request.py", line 269, in body
    raise RequestDataTooBig('Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.')
django.core.exceptions.RequestDataTooBig: Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/core/handlers/exception.py", line 39, in inner
    response = get_response(request)
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/utils/deprecation.py", line 136, in __call__
    response = self.get_response(request)
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = response_for_exception(request, exc)
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/core/handlers/exception.py", line 76, in response_for_exception
    response = debug.technical_500_response(request, *sys.exc_info(), status_code=400)
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/views/debug.py", line 84, in technical_500_response
    html = reporter.get_traceback_html()
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/views/debug.py", line 328, in get_traceback_html
    c = Context(self.get_traceback_data(), use_l10n=False)
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/views/debug.py", line 304, in get_traceback_data
    'filtered_POST': self.filter.get_post_parameters(self.request),
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/views/debug.py", line 167, in get_post_parameters
    return request.POST
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/core/handlers/wsgi.py", line 128, in _get_post
    self._load_post_and_files()
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/http/request.py", line 311, in _load_post_and_files
    self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
  File "/opt/django/djvenv/lib/python3.4/site-packages/django/http/request.py", line 269, in body
    raise RequestDataTooBig('Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.')
django.core.exceptions.RequestDataTooBig: Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.

During handling of the above exception, another exception occurred:
...

UPD:中间件被调用,但不返回数据。这让我有点生气。

我做了什么: 按照 Thameem 的建议创建了一个中间件:

from django.http import JsonResponse, HttpResponse
from django.shortcuts import render
from django.core.exceptions import RequestDataTooBig

class CheckRequest(object):

    def __init__(self, get_response):
        print('middleware init')
        self.get_response = get_response

    def __call__(self, request):
        print('middleware call')

        response = self.get_response(request)

        return response

    def process_exception(self, request, exception):
        print('middleware process exeption', exception)
        if isinstance(exception, RequestDataTooBig):
            print('CALLED')
            return HttpResponse("dummy", content_type="text/plain")
            #return JsonResponse({"error":"file is too big"})

我读到中间件必须返回 HttpResponse 或 None,所以我稍微更改了代码。 JsonResponse 也不能正常工作。

为了测试 André Cruz 的建议,我禁用了所有其他中间件:

MIDDLEWARE = [
    'projectname.generator.custom_middleware.CheckRequest',
    #'django.middleware.security.SecurityMiddleware',
    #'django.contrib.sessions.middleware.SessionMiddleware',
    #'django.middleware.common.CommonMiddleware',
    #'django.middleware.csrf.CsrfViewMiddleware',
    #'django.contrib.auth.middleware.AuthenticationMiddleware',
    #'django.contrib.messages.middleware.MessageMiddleware',
    #'django.middleware.clickjacking.XFrameOptionsMiddleware',
    #'social_django.middleware.SocialAuthExceptionMiddleware',
]

我还尝试上下移动中间件,故意使用其他中间件等,没有任何改变。

从那时起,我可以看到正在捕获错误:

Starting development server at http://0:80/
Quit the server with CONTROL-C.
middleware init
middleware init
middleware call
middleware process exeption Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.
CALLED
[20/Apr/2017 19:05:55] "POST /text_generator/new_task/ HTTP/1.1" 200 5

但是浏览器得不到响应:

enter image description here enter image description here

最佳答案

最后,在探索源代码之后,我发现在这种罕见的情况下,请求对象没有主体。

https://github.com/django/django/blob/master/django/http/request.py

self._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")

            # 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):
                # ---------------- self._body is not exists at the moment
                raise RequestDataTooBig('Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.')

            try:
                self._body = self.read()
            except IOError as e:
                raise UnreadablePostError(*e.args) from e
            self._stream = BytesIO(self._body)
        return self._body

我谦虚的解决方案只是添加它的创作。 添加它之后,一切都按预期开始工作 - 中间件返回响应并且浏览器看到它( 它已作为错误报告给 Django 开发人员:https://code.djangoproject.com/ticket/28106 ).

 @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):
                self._body = self.read(None) # <------------- FAST FIX
                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

然后您可以创建自定义中间件:

from django.http import HttpResponse
from django.conf import settings
from django.core.exceptions import RequestDataTooBig


class CheckRequest(object):

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        try:
            body = request.body
        except RequestDataTooBig:
            return HttpResponse("dummy", content_type="text/plain")

        response = self.get_response(request)
        return response

并将其添加到列表的末尾

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'social_django.middleware.SocialAuthExceptionMiddleware',
    'projectname.appname.custom_middleware.CheckRequest',
]

关于python - Django:捕获 RequestDataTooBig 异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43496658/

相关文章:

python - 为什么 `print hashlib.sha224(some_string).digest()` 与其 `repr()` 不同?

python - Django-Haystack/Whoosh - 重建索引错误

python - Wagtail 根据登录用户的权限过滤页面子元素

node.js - Redux 显示来自 Nodejs 后端的错误消息

python - Numpy 错误 : underflow encountered in exp

python - 如何提取python错误输出的信息

python - 为什么 PyCrypto 不允许使用私钥编码和使用公钥解码来验证发送者身份

php - PHP-返回多个状态,而不仅仅是true/false

python - 在 Python 连接期间与 MySQL 服务器失去连接

python - Django 模型实例是否可哈希?