由于 CORS 问题,Angular 应用程序无法读取 Django Rest Framework API

VMF*_*VMF 2 python cors django-rest-framework angular

我在使用 Angular 6 应用程序中的 Django Rest Framework REST API 时遇到 CORS 问题。

该 API 在http://localhost:55098/admin上运行。当我用 Insomnia 调用它时它工作得很好。Angular 应用程序在http://localhost:4200上运行。

当我第一次输入http://localhost:4200/cgestores时,它应该重定向到http://localhost:55098/admin/cgestores,并且确实如此,但我收到了 CORS 错误消息(见下文)。

我的配置:

REST API:我使用Python 3.7(64位),Django(2.1.5)和Django Rest Framework(3.9.1)

我的设置.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',
]
Run Code Online (Sandbox Code Playgroud)

Angular 客户端:我正在使用 Angular 6.4.1。服务(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/';
Run Code Online (Sandbox Code Playgroud)

方法getMock()返回一个固定的 JSON 值仅用于测试目的,并且工作正常。真正调用API的方法是getUsers(), 是触发CORS错误的方法:

从源“ http://localhost: 4200 ”访问位于“http://localhost:55098/admin/cgestores/”的XMLHttpRequest已被 CORS 策略阻止:不存在“Access-Control-Allow-Origin”标头所请求的资源。

(即 Chrome;Firefox 和 Edge 返回类似的响应)

这似乎是很多人的问题,我检查了这些文章:

...还有更多,还有这个视频:

...他们几乎都说了同样的话:这不是 Angular 问题,而是服务器问题,我应该对我的 DRF 项目进行以下修改:

  1. 安装 CORS 标头包

  2. 使用以下内容修改 settings.py:

    • 添加 CORS_ORIGIN_WHITELIST
    • 将 corsheaders 添加到 INSTALLED_APPS
    • 将 corsheaders.middleware.CorsMiddleware 添加到 MIDLEWARE_CLASSES

一切都完成了,但还是没有成功。在尝试了所有这些之后,我了解到我可以在响应中指定我需要的标头。所以我在我的 DRF 项目中尝试了这个:

return Response(data=pDatos, status=pStatus, headers={"Access-Control-Allow-Origin":"*"})
Run Code Online (Sandbox Code Playgroud)

...宾果游戏,它成功了!

然而,这并不是真正的解决方案,我不喜欢必须依赖于特定响应的特定参数。而且,最重要的是,我无法限制谁可以访问该网站,谁不能访问该网站,这使用 CORS_ORIGIN_WHITELIST 非常容易做到(好吧,我可以,但是编写一个包含网站列表的参数星号符号似乎不是正确的选择)。

那么,我做错了什么?为什么我的settings.py选项不起作用?我把它们注释掉了,然后就离开了return Response...,它仍然有效,所以并settings.py没有真正做任何事情。

抱歉发这么长的帖子。有任何想法吗?在此先感谢您的帮助。

小智 5

首先使用 pip 安装 django-cors-headers

pip install django-cors-headers
Run Code Online (Sandbox Code Playgroud)

您需要将其添加到项目的 settings.py 文件中:

INSTALLED_APPS = (
    ##...
    'corsheaders'
)
Run Code Online (Sandbox Code Playgroud)

接下来需要将 corsheaders.middleware.CorsMiddleware 中间件添加到 settings.py 中的中间件类中

MIDDLEWARE = (
    'corsheaders.middleware.CorsMiddleware',
    #...
)
Run Code Online (Sandbox Code Playgroud)

然后,您可以通过添加以下设置为所有域启用 CORS

CORS_ORIGIN_ALLOW_ALL = True
Run Code Online (Sandbox Code Playgroud)

或者仅对指定域启用 CORS:

CORS_ORIGIN_ALLOW_ALL = False

CORS_ORIGIN_WHITELIST = (
    'http://localhost:8000', # Allowed host, if CORS_ORIGIN_ALLOW_ALL is False
)
Run Code Online (Sandbox Code Playgroud)