How to add an HTTP header to all Django responses - django

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

Related

How can I handle POST data in django middleware?

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

__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.

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.

request.POST returns old values after updating it in custom middleware - django 1.11.9

I am using django 1.11.9
I want to add client_id and client_secret to the django POST request.
Here is how my middleware.py file looks like:
class LoginMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# auth_header = get_authorization_header(request)
# Code to be executed for each request before
# the view (and later middleware) are called.
#Add Django authentication app client data to the request
request.POST = request.POST.copy()
request.POST['client_id'] = '12345678'
request.POST['client_secret'] = '12345678'
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
Middleware is being successfully processed when I check it with a debugger. Thought when a view is called the 'client_id' and 'client_secret' fields are missing in the request.
After some experimenting i figure out that request is not getting updated and when it is called in a different view, it returns old values.
I am later using request in rest_framework_social_oauth2. And this is the point when 'client_id' and 'client_secret' disappear.
class ConvertTokenView(CsrfExemptMixin, OAuthLibMixin, APIView):
"""
Implements an endpoint to convert a provider token to an access token
The endpoint is used in the following flows:
* Authorization code
* Client credentials
"""
server_class = SocialTokenServer
validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS
oauthlib_backend_class = KeepRequestCore
permission_classes = (permissions.AllowAny,)
def post(self, request, *args, **kwargs):
import pdb ; pdb.set_trace()
# Use the rest framework `.data` to fake the post body of the django request.
request._request.POST = request._request.POST.copy()
for key, value in request.data.items():
request._request.POST[key] = value
url, headers, body, status = self.create_token_response(request._request)
response = Response(data=json.loads(body), status=status)
for k, v in headers.items():
response[k] = v
return response
I need to add client_id and client_secret to the request body, so it can be later used by rest_framework_social_oauth2.
What could be the problem? How to properly update the request?
As you're working with request and processing a request, you have to implement process_request method, so the result will be something like:
class LoginMiddleware(object):
def process_request(self, request):
request.session['client_id'] = '12345678'
and then in your view:
def your_view(request):
client_id = request.session['client_id']

Django: how to set content-type header to text/xml within a class-based view?

I'm trying to do it this way, but it doesn't work.
class MyView(View):
def options(self, request, *args, **kwargs):
"""
Handles responding to requests for the OPTIONS HTTP verb.
"""
response = http.HttpResponse()
if self.kwargs.has_key('xml'):
response['Content-Type'] = 'text/xml; charset=utf-8'
return response
You don't need to write additional code. Use TemplateResponseMixin and set content_type attribute to whatever you need:
class MyView(TemplateResponseMixin):
content_type='application/xml'
...
I think the key point is render_to_response in django.views.generic.base , whose code is this:
def render_to_response(self, context, **response_kwargs):
"""
Returns a response, using the `response_class` for this
view, with a template rendered with the given context.
If any keyword arguments are provided, they will be
passed to the constructor of the response class.
"""
response_kwargs.setdefault('content_type', self.content_type) # key
return self.response_class(
request=self.request,
template=self.get_template_names(),
context=context,
**response_kwargs
)
As for your case, May be you need this code:
class MyView(ListView):
def get(self, request, *args, **kwargs):
context = self.get_context_data()
if self.kwargs.has_key('xml'):
return self.render_to_response(context, content_type="text/xml; charset=utf-8")
return self.render_to_response(context)
I made a middleware class based off of django-cors-headers so I could allow iframe-ing of part of my django app. I keep a middleware.py in my main project directory and save a couple random middleware classes I have made there, like this one here and a ForceResponse Exception for example.
import re
from django import http
from django.conf import settings
class XFrameAllowMiddleware(object):
def process_request(self, request):
"""
If CORS preflight header, then create an
empty body response (200 OK) and return it
Django won't bother calling any other request
view/exception middleware along with the requested view;
it will call any response middlewares
"""
if (self.is_enabled(request) and
request.method == 'OPTIONS' and
"HTTP_ACCESS_CONTROL_REQUEST_METHOD" in request.META):
response = http.HttpResponse()
return response
return None
def process_response(self, request, response):
if self.is_enabled(request):
response['X-Frame-Options'] = 'ALLOWALL'
return response
def is_enabled(self, request):
return re.match(settings.XFRAME_URLS_REGEX, request.path)
Add it to your MIDDLEWARE_CLASSES and configure the regex in your settings:
MIDDLEWARE_CLASSES = (
...
'your_django_app.middleware.XFrameAllowMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...
)
XFRAME_URLS_REGEX = r'^/iframe_this_url/.*$'
from the django-cors-headers read.me:
CORS_URLS_REGEX: specify a URL regex for which to enable the sending of CORS headers; Useful when you only want to enable CORS for specific URLs, e. g. for a REST API under /api/.
Example:
CORS_URLS_REGEX = r'^/api/.*$'
Default:
CORS_URLS_REGEX = '^.*$'