Skip to main content
 首页 » 编程设计

python之Django:捕获 RequestDataTooBig 异常

2024年08月20日30telwanggs

有没有办法在 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 

但是浏览器得不到响应:

请您参考如下方法:

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

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', 
]