Create response middleware in django

A response middleware is called just before returning the response to the client. If you want to modify the response before returning it, you can create a response middleware. A response middleware is also useful when you want to return some predefined parameters in the response or we can say that a response middleware is used as a response formatter class.

To create a response middleware, go to app directory and create a file customResponseMiddlewareMixin.py in app directory. In this example, we created a django app with ‘api’. 

from django.utils.deprecation import MiddlewareMixin
import json

class MyMiddlewareClass(MiddlewareMixin):
   
   def process_response(self, request, response):
       # Process the response
       customResponse = {}     
       responseData = json.loads(response.content.decode('utf-8'))   
   
       #this is to check whether data key is returned in the response or not
       # if data key is already returned in the response then don't add data key in the customResponse dict
       # else add data key in the response if not added
       if 'data' in responseData:
           customResponse = json.loads(response.content.decode('utf-8'))   
       else:
           customResponse['data'] = json.loads(response.content.decode('utf-8'))
       customResponse['status_code'] = 201
       customResponse['status'] = True
       #print("response:", customResponse)
       response.content = json.dumps(customResponse).encode('utf-8')   
       return response

type of response.content is bytes. So here are the steps used to modify the content.

  1. Get and assign the response.content to a temporary dictionary, you need to use json.loads() to load the json Data. type(response.content) is “bytes”.
  2. Add elements to dictionary like status_code, message, status etc.
  3. Use json.dump to convert the temp dictionary back to JSON
  4. Re-assign modified response to response.content. Re-assignment is necessary as you can use “return response” only. You can not use return “response.content”.

Register/Add middleware in project settings.py file in MIDDLEWARE list at the end ‘api.customResponseMiddlewareMixin.MyMiddlewareClass’

Note: here “api” is the django app name created under the django project.

 

This approach will use the middleware in every app you created under django project as you added the middleware in the project’s settings.py file. For example, if you add/register the middleware in project settings.py file then django admin will also use this middleware. Django admin will throw an error as it expect a different response and you modify the response object in the middleware. If you want to use the middleware(newly created) in any specific app, You can check the requested url in the middleware and add conditions according to your requirement. For example, If you want that the response middleware you created will modify the response for API response only then you can add a check whether the requested url is an API url. Modify the “customResponseMiddlewareMixin.py” middleware as shown below:

from django.utils.deprecation import MiddlewareMixin
import json

class MyMiddlewareClass(MiddlewareMixin):
   def process_response(self, request, response):
       # Process the response
       #modify the response if the requested url is an API url
       if 'api' in request.path:
           customResponse = {}     
           responseData = json.loads(response.content.decode('utf-8'))   
       
           #this is to check whether data key is returned in the response or not
           # if data key is already returned in the response then don't add data key in the customResponse dict
           # else add data key in the response if not added
           if 'data' in responseData:
               customResponse = json.loads(response.content.decode('utf-8'))   
           else:
               customResponse['data'] = json.loads(response.content.decode('utf-8'))
           customResponse['status_code'] = 201
           customResponse['status'] = True
           response.content = json.dumps(customResponse).encode('utf-8')   
       return response

App’s views.py file: 

class Instructors(APIView):
   """
   List all snippets, or create a new snippet.
   """

   def get(self, request, *args, **kwargs):
       queryset = Instructor.objects.all()
       resultSet = InstructorSerializer(queryset, many=True)

       resList = {}
       resList['data'] = resultSet.data
       resList['message'] = "success"
       return Response(resList)

API response:

{
"data": [{
          "id": 1,
          "name": "Trainer 1"
          }, {
          "id": 2,
          "name": "Trainer 2"
         }],
"status": true,
"message": "success",
"status_code": 201
}

Alternative solution to response middleware

Another option is to add a common global definition which will format the response data. Call the definition before returning the response. Here is the example for this:

In app/views.py

#common response Formatter
# this function does not belong to any class. Define this definition before all the classes.
def formatter(serializedData, status = "", status_code = "", message = ""):
   resList = {}
   if 'data' in serializedData:
       resList = serializedData
   else:
       resList['data'] = serializedData

   if not status:
       resList['status'] = True
   if not status_code:
       resList['status_code'] = 200
   if not message:
       resList['message'] = "success"

   return resList

#class to List the data

class CentersList(APIView):
   """
   List all goals.
   """

   def get(self, request, *args, **kwargs):
       queryset = Center.objects.all()
       resultSet = CenterSerializer(queryset, context={"request": request}, many=True)

       resList = formatter(resultSet.data)
       return Response(resList)

Here is the response of the API:

{
	"data": [{
		"id": 1,
		"name": "Test",
		"extra_field": "this is extra field",
		"custom_title": " asdasdsadsaasdas"
	}],
	"status": true,
	"message": "success",
	"status_code": 200
}

Another example is when you have mixed data in the response and you already have the “data” key.

class CentersList(APIView):

   """
   List all goals.
   """

   def get(self, request, *args, **kwargs):
       queryset = Center.objects.all()
       resultSet = CenterSerializer(queryset, context={"request": request}, many=True)

       resList = {}
       resList['data'] = resultSet.data
       resList = formatter(resList)
       return Response(resList)
{
	"data": [{
		"id": 1,
		"name": "Test",
		"extra_field": "this is extra field",
		"custom_title": " asdasdsadsaasdas"
	}],
	"status": true,
	"message": "success",
	"status_code": 200
}

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to Top
Shares