3

I am creating Django middleware for blocking a user when (s)he gets throttled more than 5 times but I am getting ContentNotRenderedError.

Full error msg:

Traceback (most recent call last):
  File "/home/raptor/Application/utilities/anaconda3/envs/slic4rapi/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/home/raptor/Application/utilities/anaconda3/envs/slic4rapi/lib/python3.8/site-packages/django/utils/deprecation.py", line 119, in __call__
    response = self.process_response(request, response)
  File "/home/raptor/Application/utilities/anaconda3/envs/slic4rapi/lib/python3.8/site-packages/django/middleware/common.py", line 113, in process_response
    response.headers['Content-Length'] = str(len(response.content))
  File "/home/raptor/Application/utilities/anaconda3/envs/slic4rapi/lib/python3.8/site-packages/django/template/response.py", line 126, in content
    raise ContentNotRenderedError(
django.template.response.ContentNotRenderedError: The response content must be rendered before it can be accessed.
[22/Mar/2022 11:55:03] "GET /api/v1/userdetail/ HTTP/1.1" 500 84321

Middleware class

class BlockMiddleware:
    
    def __init__(self, get_response):
        self.get_response = get_response
        # some other variables
    
    def __call__(self, request):
        # handle_blocking will return "None" if user can access application else "rest_framework.response.Response" object
        blocking_res = self.handle_blocking(request)
        if blocking_res:
            return blocking_res

        response = self.get_response(request)
        # logic for counting how many throttles have left
        # then
        if throttles_left <= 0:
            return Response(
                    data='User is blocked due to exceeding throttles limit.',
                    status=status.HTTP_403_FORBIDDEN
                )
        else:
            return response

Example return of handle_blocking function:

return Response(
    data='User is blocked, please contact the support team.',
    status=status.HTTP_403_FORBIDDEN
)

It works when I remove the middleware and don't use the Response class(return the self.get_response(request) instead). I am unable to figure out the error. What am I doing wrong?

rain
  • 525
  • 6
  • 15

1 Answers1

6

If you really want to return a Response instance, you need to set some properties before returning it:

from rest_framework.renderers import JSONRenderer

class BlockMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        blocking_res = self.handle_blocking(request)
        if blocking_res:
            return blocking_res

        response = self.get_response(request)
        if throttles_left <= 0:
            response = Response(
               data='User is blocked due to exceeding throttles limit.',
               status=status.HTTP_403_FORBIDDEN
            )
            response.accepted_renderer = JSONRenderer()
            response.accepted_media_type = "application/json"
            response.renderer_context = {}
            response.render()
            return response
        else:
            return response

You need to do the same also in your handle_blocking implementation, for example:

from rest_framework.renderers import JSONRenderer
response = Response(
    data='User is blocked, please contact the support team.',
    status=status.HTTP_403_FORBIDDEN
)
response.accepted_renderer = JSONRenderer()
response.accepted_media_type = "application/json"
response.renderer_context = {}
response.render()
return response

These properties are generally set by the api_view decorator that cannot be used with middlewares, so you must set them manually.

Alain Bianchini
  • 3,883
  • 1
  • 7
  • 27
  • Hi, thanks for the response. I already tried `django.http.JsonResponse` and it was working, but the problem is with `rest_framework.response.Response`, I have other custom middleware and it's working fine there. – rain Mar 22 '22 at 17:51
  • @rain I have edited my answer, now it works with `Response` – Alain Bianchini Mar 22 '22 at 18:19
  • Not working, prompting the same error, but yeah, it is happening because of the `api_view` decorator. Please revert your answer, accepting that would be more appropriate. – rain Mar 23 '22 at 18:34
  • @rain Try to call `render` on the `Response` instance after having set the other properties (I have updated my answer to show this) – Alain Bianchini Mar 23 '22 at 18:35
  • Yes, that worked. Can you share any doc/reference link which you used? – rain Mar 23 '22 at 18:53
  • @rain Great. Sure I read the documentation about the [Response object](https://www.django-rest-framework.org/api-guide/responses/) and then I tried to set various `Response` properties – Alain Bianchini Mar 23 '22 at 19:12