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:
- https://github.com/crs4/ome_seadragon/wiki/Enable-Django-CORS-(Cross-Origin-Resource-Sharing)-Headers-configuration
- https://www.techiediaries.com/django-cors/
- CORS django 'Access-Control-Allow-Origin'
- No 'Access-Control-Allow-Origin' using django rest framework api
- Django Python rest framework, No 'Access-Control-Allow-Origin' header is present on the requested resource in chrome, works in firefox
- Django Rest Framework: XMLHttpRequest cannot load http://127.0.0.1:8000/xyz/api/abc
...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:
- Add CORS_ORIGIN_WHITELIST
- Add corsheaders to INSTALLED_APPS
- Add corsheaders.middleware.CorsMiddleware to MIDLEWARE_CLASSES
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.
manage.py makemigrations corsheaders; manage.py migrate corsheaders
. – Rémi HéneaultMake migrations...
andMigrate
context menu options to apply any pending changes, but it didn't detect any. Still not working. – VMF