Reputation: 73
I'm experiencing a CORS issue consuming a Django Rest Framework REST API from an Angular 6 app.
The API runs on http://localhost:55098/admin. It works fine when I invoke it with Insomnia. Angular app runs on http://localhost:4200.
When I first typed http://localhost:4200/cgestores it should redirect to http://localhost:55098/admin/cgestores, and it did, but I got a CORS error message (see below).
My configuration:
REST API: I'm using Python 3.7 (64 bit), Django (2.1.5) and Django Rest Framework (3.9.1)
My settings.py:
ALLOWED_HOSTS = [
'testserver',
'localhost'
]
CORS_ORIGIN_ALLOW_ALL = True
INSTALLED_APPS = [
# Add your apps here to enable them
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework_swagger',
'corsheaders',
'admin_v20',
]
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Angular client: I'm using Angular 6.4.1. The service (apiusuarios.service.ts):
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse, HttpParams } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class ApiusuariosService {
datos: iCentroGestor;
constructor(private http: HttpClient) { }
getMock(): any{
return [
{"gececo":"a", "gecdes":"one"},
{"gececo":"b", "gecdes":"two"},
{"gececo":"c", "gecdes":"three"}
];
}
getUsers(): Observable<any> {
return this.http.get(endpoint + 'cgestores').pipe(map(this.extractData));
}
}
export interface iCentroGestor {
gececo: string;
gecdes: string;
}
const endpoint = 'http://localhost:55098/admin/';
Method getMock()
returns a fixed JSON value just for test purposes, and it works fine. The method that actually calls the API is getUsers()
, and is the method that triggers the CORS error:
Access to XMLHttpRequest at 'http://localhost:55098/admin/cgestores/' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
(that's Chrome; Firefox and Edge return similar responses)
This seemed to be an issue for a lot of people, I checked these articles:
...and a few more, and also this video:
...and they all pretty much sayed the same: it's not an Angular problem, it's a server issue, and I should make the following modifications to my DRF project:
Install CORS headers package
Modify settings.py with the following:
All done, but it still didn't work. After trying all that, I read that I could specify the header that I need on my response. So I tried this in my DRF project:
return Response(data=pDatos, status=pStatus, headers={"Access-Control-Allow-Origin":"*"})
... and bingo, it worked!
However, this is not really a solution, I don't like to have to depend on a specific parameter on a specific response. And, on top of that, I have no way to restrict who can access the site and who can't, which would be really easy to do with CORS_ORIGIN_WHITELIST (well, I could, but writing a parameter with a list of sites in place of the asterisk symbol doesn't seem to be the right option).
So, what am I doing wrong? Why do my settings.py
options not work? I commented them out, and just left the return Response...
and it still works, so settings.py
is not really doing anything.
Sorry for this long post. Any ideas? Thanks in advance for your help.
Upvotes: 2
Views: 4746
Reputation: 71
Start by installing django-cors-headers using pip
pip install django-cors-headers
You need to add it to your project settings.py file:
INSTALLED_APPS = (
##...
'corsheaders'
)
Next you need to add corsheaders.middleware.CorsMiddleware middleware to the middleware classes in settings.py
MIDDLEWARE = (
'corsheaders.middleware.CorsMiddleware',
#...
)
You can then, either enable CORS for all domains by adding the following setting
CORS_ORIGIN_ALLOW_ALL = True
Or Only enable CORS for specified domains:
CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = (
'http://localhost:8000', # Allowed host, if CORS_ORIGIN_ALLOW_ALL is False
)
Upvotes: 7
Reputation: 73
Found the answer myself, I'd like to share it.
I'm using Django 2.1.5. It appears that section MIDDLEWARE_CLASS
is simply called MIDDLEWARE
since version 2. When I changed it I was forced to comment out django.contrib.auth.middleware.SessionAuthenticacionMiddleware
, and that did the magic. Now everything works as it should.
Upvotes: 1