How can I handle POST data in django middleware? - django

I have Django middleware to handle POST request.
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, req):
response = self.get_response(req)
# want to do something with 'r.body',
# but it is not able to be read
return response
As the request body is already read in get_response, I cannot read it again in middleware.
Tried copy.copy(), but no luck since the copied stream references same object of the original one. copy.deepcopy() raises an exception.
How can I handle POST data in middleware?
I want to handle all requests, so implementing the logic in every view is not ideal.

Found solution
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, req):
req.body # just add this line BEFORE get_response
response = self.get_response(req)
return response

Related

Implement Django middleware for response behaviour

I'm a bit confused by the middleware description in the official django docs and can't seem to wrap my head around it.
When implementing class-based middleware it looks something like this (ex. from docs):
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
The middleware gets called for requests as well as for response. This is my first question:
How do I implement different behavior depending on if I'm processing a request or a response?
Also, and this is related, I'm a bit confused because there also seems to be an old deprecated way of writing middleware where it was possible to overwrite the methods process.request() and process_response(). I'm not sure if this way of writing middleware is inadvisable now. Usually, the Django docs are spot on but this part is a little confusing (to me).
Can someone clear this up for me?
Basically the codes before responses = self.get_response(request) should process the request. self.get_response() will get the response from view, then codes after that should process the response. Finally you need to return the response from the function. Here is a visualization:
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Codes here will process the request
# -----------------------------------
response = self.get_response(request)
# -----------------------------------
# Codes here will process the response
return response

__init__() missing 1 required positional argument: 'get_response'

I use Django 1.11 and got this error
class TenantMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
set_tenant_schema_for_request(request)
response = self.get_response(request)
return response
I tried to fix that:
class TenantMiddleware:
def process_response(self, request, response):
set_tenant_schema_for_request(request)
return response
is this a correct equivalent using "new" middleware style?
Your first example is example of new-style middleware, and second one is example of old-style middlewares. If you still want to use process_response(request, response) in new-style middlewares you should use django.utils.deprecation.MiddlewareMixin class, as a parent class of your middleware class.
from django.utils.deprecation import MiddlewareMixin
# old-style middleware usage in new versions of django
class TenantMiddleware(MiddlewareMixin):
def process_response(self, request, response):
set_tenant_schema_for_request(request)
return response
If you use new-style middlewares,,,
# new-style middleware usage and explanation
class TenantMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# implement process_request(request) logic here
response = self.get_response(request)
# implement process_response(request, response) logic here
return response # returns HttpResponse or subclasses here.
In new-style middlewares, code before self.get_response(request) is same as process_request(request) and code after self.get_response(request) is same as process_response(request, response).
Hope, it helps you.

Pass extra arguments from middleware to view

I want to add another field in my POST(JSON) request. The key-value pair has to be "request id". I have concluded that the best way would be to generate a random request id and inject it into the request object using middleware. I have written a custom middleware for this. I am getting an error when I try to hit the endpoint.
I have tried searching through the internet but haven't found a solution to my error.
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
req = json.loads(request.body)
req_id = random.randint(0,1000000)
req["req_id"]=req_id
response = self.get_response(req)
# Code to be executed for each request/response after
# the view is called.
return response
The error I am getting is 'dict' object has no attribute 'is_ajax'.
Can you please help me fix this and if there is an easier and better way to implement this, please let me know.
Okay. I achieved what I was trying to do. My code for custom middleware is:
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
def process_view(self, request, view_func, *view_args, **view_kwargs):
request.req_id = random.randint(0,1000000)
Update: I was a noob back then and now reading the comments I feel embarrassed :P

How can I add field to request body in django middleware

I want to add new field in request body in middleware and use it in views.
I googled it but the results was not worked.
How can I do it?
Django v2 python 3.6
You need to add a separate custom middleware which will process the request before landing to a particular view. Below is the code for custom middleware:
class ProcessRequestMiddleware(MiddlewareMixin):
"""
This middleware appends new payload in request body
"""
def process_view(self, request, view_func, *view_args, **view_kwargs):
request_data = getattr(request, '_body', request.body)
request_data = json.loads(request_data)
# here you can write the logic to append the payload to request data
request._body = json.dumps(request_data)
return None
Note - The middleware was place inside an app common (common/middleware/custommiddleware.py)
Now add this middleware to settings.MIDDLEWARE list:
"common.middleware.custommiddleware.ProcessRequestMiddleware"
Now you can retrieve the payload, which you had appended in custommiddleware, inside any of the views you want by just calling the json.loads(request.body).
Try following code:
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
my_request = request.GET.copy()
my_request['foo']='bar'
request.GET = my_request
response = self.get_response(request)
return response
I tried this for you: added above code into: example.py Then added 'example.SimpleMiddleware', into MIDDLEWARE
My view method:
def index(request):
for key in request.GET:
print (key, '--->', request.GET[key])
return render(request, 'example.html')
able to print foo ---> bar browser sends the request.

How to add an HTTP header to all Django responses

I'd like to add a few headers to all responses that my Django website returns. Is there a way to do this (besides adding a wrapper to the render function)?
Yes, you should have a look at middlewares.
yourapp/middleware.py
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
response['X-My-Header'] = "my value"
return response
yourproject/settings.py
MIDDLEWARE = [
...,
'yourapp.middleware.MyMiddleware',
...,
]
When returning JsonResponse.
from django.http import JsonResponse
data = {'key','value'} # some data
response = JsonResponse(data,status=200)
response['Retry-after'] = 345 # seconds
response['custom-header'] = 'some value'
return response