I have an application in Django 1.9 that uses SessionMiddleware. I would like to create an API for this application inside the same project, but when doing a POST request it
DO NOT USE csrf_exempt
with Django REST framework.
This won't work because the SessionAuthentication enforces the csrf check anyway.
Please make sure you use the csrf token in your AJAX requests. Django has a comprehensive documentation about it
I found out the way to solve this. You need to create a middleware that calls before any Session Middlewares and then check against your desired urls or app to exempt the CSRF token validation. So, the code would be like this:
settings.py
MIDDLEWARE_CLASSES = [
'api.middleware.DisableCSRF', # custom middleware for API
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'a9.utils.middleware.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'a9.core.access.middleware.AccessMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
]
urls.py
app_name = "api"
urlpatterns = [
url(r'^v1/', include([
url(r'^', include(router.urls)),
url(r'^auth/', MyAuthentication.as_view()),
url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
url(r'^admin/', include(admin.site.urls)),
])),
]
csrf_disable.py
from django.core.urlresolvers import resolve
class DisableCSRF(object):
"""Middleware for disabling CSRF in an specified app name.
"""
def process_request(self, request):
"""Preprocess the request.
"""
app_name = "api"
if resolve(request.path_info).app_name == app_name:
setattr(request, '_dont_enforce_csrf_checks', True)
else:
pass # check CSRF token validation
This will only check CSRF token against a specific app or url without removing all the CSRF. Also, this is django-rest-framework independent :)
You need to decorate the csrf_exempt
inside the dispatch
method.
class MyView(FormView):
@method_decorator(csrf_exempt)
def dispatch(self, *args, **kwargs):
return super(MyView, self).dispatch(*args, **kwargs)
def post(self, request, *args, **kwargs):
# ....
return super(MyView, self).post(request, *args, **kwargs)