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
Related
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
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.
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
I have some Internet Explorer problems with my Angular 5 app, because in the
IE 11 my get-requests to a django-REST backend are cached by the browser.
I found a question suggesting to add special cache-control-headers to the response, but I didn't find a working answer how to do it with Django REST. All other browsers I have tested seem to work without problems.
Perhaps you could add the Cache-Control header to all responses using a middleware class.
class CacheControlMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
response['Cache-Control'] = 'no-cache'
return response
and then wire that into your settings.py
MIDDLEWARE = [
...
'your.app.CacheControlMiddleware',
...
]
Bear in mind that will apply to all views (all your ModelViewSets), which you may or may not want.
If you want to apply it only to specific view sets, you may be better to override the retrieve() and list() methods in your view sets where you can set the Cache-Control header on the Response. For more info on that see http://www.django-rest-framework.org/api-guide/generic-views/#mixins
I had the same problem.
Adding the 'Cache-Control' header to the response worked for me.
response = Response()
response['Cache-Control'] = 'no-cache'
An example views.py:
from rest_framework import status
from rest_framework.response import Response
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
def snippet_list(request):
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
response = Response(serializer.data)
response['Cache-Control'] = 'no-cache'
return response
The other answers have the right idea, but here is a slightly more refined solution.
def disable_cache_middleware(get_response):
def middleware(request):
response = get_response(request)
response['Cache-Control'] = 'no-cache, no-store, must-revalidate'
return response
return middleware
The main difference here is that no-store and must-revalidate are included in the header as well. The Mozilla documentation on the header suggests this combination to prevent browser caching. You can read more about the extra header values there.
Secondly, I just find that the function-based middleware results in a bit less boilerplate than the class-based solution in this case.
When i read Django documentation i found only handlers for:
400,
403,
404,
500 errors.
Why 405 error handler doesn't exists but decorators like require_POST() is in common use?
The point is, what is proper way to create custom error page for Method Not Allowed error ?
I resolve my problem using Django Middleware maybe this will help someone
from django.http import HttpResponseNotAllowed
from django.shortcuts import render
class HttpResponseNotAllowedMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
if isinstance(response, HttpResponseNotAllowed):
return render(request, '405.html', status=405)
return response
If you want a different response for each view, you can overwrite your function like:
class someView(View):
....
def http_method_not_allowed(self, request, *args, **kwargs):
return HttpResponse("Not available")
If you wanna know what the function does check https://github.com/django/django/blob/master/django/views/generic/base.py#L90