Redirect Middleware not working - Django - django

I created a simple redirect middleware
class RedirectMiddleware(object):
def urlredirect(self, request):
path = self.request.build_absolute_uri()
if "something" in path:
URL = "http://www.someurl.com"
else:
URL = "http://www.otherurl.com"
return HttpResponsePermanentRedirect(URL)
But i doesnt seem to work , it doesnt does anything , and yes i added it on the settings.py, any idea ?

You need to implement process_request
Writing your own middleware is easy. Each middleware component is a
single Python class that defines one or more of the following methods:
process_request
process_request(request)
request is an HttpRequest object.
process_request() is called on each request, before Django decides
which view to execute.
It should return either None or an HttpResponse object. If it returns
None, Django will continue processing this request, executing any
other process_request() middleware, then, process_view() middleware,
and finally, the appropriate view. If it returns an HttpResponse
object, Django won’t bother calling any other request, view or
exception middleware, or the appropriate view; it’ll apply response
middleware to that HttpResponse, and return the result.

Related

Django check CSRF token manually

I am implementing an API that works either with an API key, or with a CSRF token. The goal is for it to be usable either by a web app (protected by CSRF) or by a third party application (protected by API key).
Basically on each request (all via POST), I check if there is an API key. If there is a valid one, it's good to go. If not, I want to fall back to verifying CSRF.
Is there a function I can call to verify the CSRF myself? The view itself is #csrf_exempt because API keys need to work.
You could probably subclass the CsrfViewMiddleware class and override the process_view method. Then include your custom middleware instead of the default CSRF one.
from django.middleware.csrf import CsrfViewMiddleware
class CustomCsrfMiddleware(CsrfViewMiddleware):
def process_view(self, request, callback, callback_args, callback_kwargs):
if request.META.get('api_key'):
# process api key
else:
return super(CsrfViewMiddleware, self).process_view(...)
You can use builtin csrf verification like this:
from django.middleware.csrf import CsrfViewMiddleware
def check_csrf(request):
reason = CsrfViewMiddleware().process_view(request, None, (), {})
if reason:
# CSRF failed
raise PermissionException() # do what you need to do here
I've been accessing the CsrfViewMiddleware like Aldarund has shown but more needs to be said about this kind of solution:
If you are performing the test in a view, then you can return reason directly. According to how Django middleware works, when process_view returns something else than None, then it must be a HttpResponse object so it can just be returned by the view.
There may be cases where you do not want to return reason directly, but if there is no reason not to do so, I'd rather return it, for consistency with how the site behaves in other cases.
If you use the test in a run-of-the-mill view, and you already use CsrfViewMiddleware site-wide, it is usually the case that request will already have passed once through CsrfViewMiddleware. (Yes, it can happen. I have a case where a request I receive is modified and retested through CsrfViewMiddleWare after it has already been tested by CsrfViewMiddleware due to the site-wide configuration.) However, the middleware sets csrf_processing_done on request after it tests it and won't retest it if it is called again, because of this flag. So csrf_processing_done has to be reset to False to perform a second test.
Here's an illustration of the above:
from django.middleware.csrf import CsrfViewMiddleware
def view(request):
request.csrf_processing_done = False
reason = CsrfViewMiddleware().process_view(request, None, (), {})
if reason is not None:
return reason # Failed the test, stop here.
# process the request...
In my case, I wanted to POST some raw data with CSRF check.
So, I use this decorator requires_csrf_token in the view which process POST data :
from django.views.decorators.csrf import requires_csrf_token
#requires_csrf_token
def manage_trade_allocation_update(request):
In my template, I added csrf_token génération and put it in data post :
{% csrf_token %}
...
data['csrfmiddlewaretoken'] = document.querySelector('input[name="csrfmiddlewaretoken"]').value;
With this mecanism, I can use CSRF protection with manual HTTP POST request.

Django - Ajax registration

I am trying to allow registration (using this django-registration register view) to one of my applications from a modal dialog.
Since this form is in a modal box, I'd like to get an json reponse on success (instead of the default redirection)
How can I use this view (django-registration register) to manage the registration and send back a json response on success ?
I know how to make ajax/json responses, the question is how to use the django-registration view without the redirection behavior or wrap it into an other view to manage the response.
First you need to change the urls.py to wrap the existing view with another functionality. To do that you have to create a new backend package in backends folder and change urls.py there while keeping everything else intact, or you could just go ahead and modify the existing urls.py in the backend package.
I have not tested this, but it should work.
Point url to the new view:
# urls.py
url(r'^register/$', register_wrap,
{'backend': 'registration.backends.default.DefaultBackend'},
name='registration_register'),
# your new view that wraps the existing one
def register_wrap(request, *args, **kwargs):
# call the standard view here
response = register(request, *args, **kwargs)
# check if response is a redirect
if response.status_code == 302:
# this was redirection, send json response instead
else:
# just return as it is
return response
If you are going to need this for more views you can just create a decorator using this.
Why I would do is to check if request.is_ajax() in your normal after-successfull-registration-redirect view and return json response there.
You ask how you can use the existing view to manage the registration and send back a json response on success. Since the HttpResponseRedirect is pretty much hard coded in the view, you can't use the view as it is. Instead, either fork it, or write your own view and change the urls.py so that r'^register/$' directs to your new view.
As far as the json response is concerned, on success you can do something like this:
from django.utils import simplejson as json
def register_ajax(request):
...
return HttpResponse(json.dumps(dict(success=True, **dict_containing_data)))
Hope this helps

Django middleware and HttpRequest change

I have a middleware to make some calculations/check for each incoming request. Some of view need this calculations result.
As I do not want to call the same code twice, I would like to put results to HttpRequest in middleware, so view will be able to read it.
Could you help me with right hint, how can I add an object to HttpRequest?
thanks
HttpRequest is a normal class, you could directly assign the object to its instance, the request, in the middleware. For example:
class MyMiddleware(object):
def process_request(self, request):
request.foo = 'bar'
You can extend HttpResponse by using so-called "monkey-patch" method. For example you can easily add or replace methods and properties into HttpResponse by calling following function from within your root __init__.py or wsgi.py or even settings.py:
def apply_http_request_patch():
def get_property_value(request):
# return lazily evaluated value
from django.http import HttpRequest
HttpRequest.some_property = property(get_property_value)

How to convert a Django HttpResponse to a Django render call

I have the following code
def ajax_login_request(request):
try:
request.POST[u'login']
dictionary = request.POST
except:
dictionary = request.GET
user = authenticate(username = dictionary[u'login'], password = dictionary[u'password'])
if user and user.is_active:
login(request, user)
result = True
else:
result = False
response = HttpResponse(json.dumps(result), mimetype = u'application/json')
return response
which is being called via ajax. I'm a noob and this is from an example in a book. Unfortunately the version of Django I'm using throws a CSRF error on this. I've done the other CSRF bits, but I don't know how to change the HttpResponse bit to a render call. I do not want to use CSRF_exempt, because I have no idea when that is appropriate. Can someone please provide me the equivalent render call for the HttpResponse above.
Thanks
To make your original code work, you need to get a RequestContext object and pass it along with your response, something like this:
from django.http import HttpResponse
from django.template import RequestContext, Template
def ajax_login_request(request):
# ...
# This bit of code adds the CSRF bits to your request.
c = RequestContext(request,{'result':json.dumps(result)})
t = Template("{{result}}") # A dummy template
response = HttpResponse(t.render(c), mimetype = u'application/json')
return response
Do read up on the CSRF documentation as you might run into strange errors if you don't understand all the ways CSRF is "wired" in your app. The page also has a javascript snippet to make sure CSRF cookies are sent with your ajax requests if you are sending them without a form.
You can also use the render_to_response() shortcut, but you would need to have an actual template to load (in your case, you don't need a template, hence the "dummy" template in my example).
Ok, I'm going to re-draft this answer so you understand where I'm coming from. The CSRF middleware works like this:
You make request -------> request hits csrf --(invalid/no token)--> render 403
middleware
|
(valid token)
|
\ /
Call view
|
\ /
middleware sets
csrf cookie
|
\ /
Response appears
In other words, if you are seeing a 403 csrf page, your view was never called. You can confirm this by sticking a spurious print statement in the view and watching the output from runserver when you make your request.
To solve this, you either need to disable csrf (not good) or use one of the ajax methods available to you. If the required token is passed in your view will actually be executed.
The reason your view is not called is to prevent the action from the forged site from actually ever taking place - for example, if you denied the template at response time the user would already be logged in. The same behaviour occurs with the function decorators.
As for the middleware setting the cookie, that does not alter or depend on the render function at all - this sets the HTTP header Cookie: ... in the response. All responses in Django are HttpResponse objects until it finally converts them to output; render functions are helpers, but that's not what's causing your problem here.
Edit I'll convert what you've got to a render call. You could do this:
return render_to_response(`ajax_templates/login_response.html`,
{'loginresponse': json.dumps(result)})
Where ajax_templates/login_response.html is just:
{% loginresponse %}
That's it. HttpResponse has a main default argument which is the string to return (literally, the html of the web page); that's what you're doing initially. render_to_response and render are shortcuts to this which do this:
render_to_response called ----> open template asked for --> substitute arguments
|
\ /
django instructs web server <--- return this from view <-- create HttpResponse
to send to client object

How to display a custom error page for HTTP status 405 (method not allowed) in Django when using #require_POST

My question is simple, how do I display a custom error page for HTTP status 405 (method not allowed) in Django when using the #require_POST decorator?
I'm using the django.views.decorators.http.require_POST decorator, and when the page is visited by GET request, the console shows a 405 error, but the page is just blank (not even a Django error page). How do I get Django to display a custom and/or default error page for this kind of error?
EDIT:
It's worth mentioning that I've tried putting a 404.html, 500.html and 405.html page in my templates folder - but that does not help either. I have also varied between DEBUG = True and False, to no avail.
You have to write custom Django middleware. You can start with this one and extend it to check if 405.html file exists and so on:
from django.http import HttpResponseNotAllowed
from django.template import RequestContext
from django.template import loader
class HttpResponseNotAllowedMiddleware(object):
def process_response(self, request, response):
if isinstance(response, HttpResponseNotAllowed):
context = RequestContext(request)
response.content = loader.render_to_string("405.html", context_instance=context)
return response
Check docs if you don't know how to install middleware:
http://docs.djangoproject.com/en/dev/topics/http/middleware/
You can also check this article:
http://mitchfournier.com/2010/07/12/show-a-custom-403-forbidden-error-page-in-django/
If you look into the documentation and the source code of django.views.defaults you see that only 404 and 500 errors are supported in a way that you only have to add the 404.html resp. 500.html to your templates directory.
In the doc. you can also read the following
Returning HTTP error codes in Django
is easy. There are subclasses of
HttpResponse for a number of common
HTTP status codes other than 200
(which means "OK"). You can find the
full list of available subclasses in
the request/response documentation.
Thus if you want to return a 405 error, you have to use the HttpResponseNotAllowed class
An example
I'm not sure that's possible. Perhaps you should consider filing a bug report.
I landed here in 2022. The above accepted answer is not working for me. I use django rest framework. My sollution was to create a middleware with
#app/middleware.py
from django.http import HttpResponse
from django.template import loader
class HttpResponseNotAllowedMiddleware:
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.
if response.status_code == 405:
context = {}
template = loader.get_template('app/405.html')
return HttpResponse(template.render(context, request))
return response
then install it by adding this to settings
MIDDLEWARE = [
.......
'app.middleware.HttpResponseNotAllowedMiddleware',
]
the 405.html template is just a plain not allowed text