diff --git a/worf/renderers.py b/worf/renderers.py new file mode 100644 index 0000000..51e313b --- /dev/null +++ b/worf/renderers.py @@ -0,0 +1,40 @@ +from django.http import HttpResponse, JsonResponse +from django.template.response import TemplateResponse + +from worf.conf import settings + + +def browsable_response(request, response, status_code=200): + template = "worf/api.html" + context = dict( + content=response.content.decode("utf-8"), + response=response, + settings=settings, + ) + + response = TemplateResponse(request, template, context=context) + response.status_code = status_code + response.render() + + return response + + +def render_response(request, data, status_code=200): + is_browsable = ( + settings.WORF_BROWSABLE_API + and "text/html" in request.headers.get("Accept", "") + and request.GET.get("format") != "json" + ) + + response = ( + JsonResponse(data, json_dumps_params=dict(indent=2 if is_browsable else 0)) + if data != "" + else HttpResponse() + ) + + response.status_code = status_code + + if is_browsable: + response = browsable_response(request, response, status_code) + + return response diff --git a/worf/settings.py b/worf/settings.py index 02ef53e..00b421f 100644 --- a/worf/settings.py +++ b/worf/settings.py @@ -1,5 +1,6 @@ from django.conf import settings +DATA_UPLOAD_MAX_MEMORY_SIZE = getattr(settings, "DATA_UPLOAD_MAX_MEMORY_SIZE") WORF_API_NAME = getattr(settings, "WORF_API_NAME", "Worf API") WORF_API_ROOT = getattr(settings, "WORF_API_ROOT", "/api/") diff --git a/worf/views/base.py b/worf/views/base.py index d454371..c290c93 100644 --- a/worf/views/base.py +++ b/worf/views/base.py @@ -8,10 +8,10 @@ from django.core.exceptions import ( ImproperlyConfigured, ObjectDoesNotExist, + RequestDataTooBig, ValidationError, ) -from django.http import HttpResponse, JsonResponse -from django.template.response import TemplateResponse +from django.template.defaultfilters import filesizeformat from django.views import View from django.views.decorators.cache import never_cache from django.utils.decorators import method_decorator @@ -19,6 +19,7 @@ from worf.conf import settings from worf.casing import camel_to_snake, snake_to_camel from worf.exceptions import HTTP_EXCEPTIONS, HTTP404, HTTP422, PermissionsException +from worf.renderers import render_response from worf.serializers import LegacySerializer from worf.validators import ValidationMixin @@ -41,29 +42,7 @@ def render_to_response(self, data=None, status_code=200): msg += "render_to_response, nor did its serializer method" raise ImproperlyConfigured(msg) - is_browsable = ( - settings.WORF_BROWSABLE_API - and "text/html" in self.request.headers.get("Accept", "") - and self.request.GET.get("format") != "json" - ) - - json_kwargs = dict(json_dumps_params=dict(indent=2)) if is_browsable else {} - - response = JsonResponse(data, **json_kwargs) if data != "" else HttpResponse() - response.status_code = status_code - - if is_browsable: - template = "worf/api.html" - context = dict( - content=response.content.decode("utf-8"), - response=response, - settings=settings, - ) - response = TemplateResponse(self.request, template, context=context) - response.status_code = status_code - response.render() - - return response + return render_response(self.request, data, status_code) class AbstractBaseAPI(APIResponse, ValidationMixin): @@ -221,5 +200,9 @@ def dispatch(self, request, *args, **kwargs): return self.render_to_response( dict(message=HTTP404.message), HTTP404.status ) + except RequestDataTooBig: + self.request._body = self.request.read(None) # prevent further raises + message = f"Max upload size is {filesizeformat(settings.DATA_UPLOAD_MAX_MEMORY_SIZE)}" + return self.render_to_response(dict(message=message), HTTP422.status) except ValidationError as e: return self.render_to_response(dict(message=e.message), HTTP422.status)