Chrome中取消了飞行前选项请求(已支持CORS)

时间:2019-02-20 02:53:45

标签: django angular google-chrome http cors

我正在开发一个使用Angular + Django(Django Rest Framework)的项目。在开发过程中,CORS支持通过使用django-cors-headersCORS_ORIGIN_ALLOW_ALL = TrueCORS_ALLOW_CREDENTIALS = True来实现。

当我尝试发送POST请求以在前端(Angular)中创建一些资源时,Chrome会发送一些飞行前OPTIONS请求,并由后端服务器(python manage.py runserver)成功响应,而其他请求则没有。这些请求由于未知原因而被取消,后端服务器日志指示服务器已接收并接受了请求,详细信息如下图所示。

Some OPTIONS requests are failed

Backend server logs

失败请求的标头如下所示。

Details of failed request

但是,如果复制标题的内容并尝试使用curl发送,它将按预期工作。

$ curl -v -X OPTIONS -H "Access-Control-Request-Headers: authorization,content-type" -H "Access-Control-Request-Method: POST" -H "DNS: 1" -H "Origin: http://localhost:4200" -H "Referer: http://localhost:4200" -H "User-Agent: Mozilla/5.0" http:/localhost:8000/api/user-permissions/
* Unwillingly accepted illegal URL using 1 slash!
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 8000 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> OPTIONS /api/user-permissions/ HTTP/1.1
> Host: localhost:8000
> Accept: */*
> Access-Control-Request-Headers: authorization,content-type
> Access-Control-Request-Method: POST
> DNS: 1
> Origin: http://localhost:4200
> Referer: http://localhost:4200
> User-Agent: Mozilla/5.0
>
< HTTP/1.1 200 OK
< Date: Wed, 20 Feb 2019 02:47:39 GMT
< Server: WSGIServer/0.2 CPython/3.7.1
< Content-Type: text/html; charset=utf-8
< Content-Length: 0
< Vary: Origin
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Origin: http://localhost:4200
< Access-Control-Allow-Headers: accept, accept-encoding, authorization, content-type, dnt, origin, user-agent, x-csrftoken, x-requested-with
< Access-Control-Allow-Methods: DELETE, GET, OPTIONS, PATCH, POST, PUT
< Access-Control-Max-Age: 86400
<
* Connection #0 to host localhost left intact

有什么想法如何发生的?谢谢。


示例代码:

// The method of the component that invokes the methods of PermissionService.
  /** Update selected user's permissions. */
  updatePermissions() {
    const diff = this.diffPermissions();
    const toBeCreated = diff[0];
    const toBeDeleted = diff[1];
    this.isLoading = true;
    zip(
      this.permissionService.createUserPermissions(toBeCreated),
      this.permissionService.deleteUserPermissions(toBeDeleted),
    ).pipe(
      map(() => true),
      catchError((err: HttpErrorResponse) => {
        alert(err.message);
        return observableOf(false);
      }),
    ).subscribe(succeed => {
      this.isLoading = false;
    });
  }

// The methods of PermissionService that issue the HTTP requests.

  createUserPermission(req: UserPermissionRequest) {
    return this.http.post(`${environment.API_URL}/user-permissions/`, req);
  }

  createUserPermissions(reqs: UserPermissionRequest[]) {
    // TODO(youchen): Evaluate the performance cost.
    return forkJoin(reqs.map(req => this.createUserPermission(req)));
  }

  deleteUserPermission(permissionId: number) {
    return this.http.delete(`${environment.API_URL}/user-permissions/${permissionId}/`);
  }

  deleteUserPermissions(permissionIds: number[]) {
    // TODO(youchen): Evaluate the performance cost.
    return forkJoin(permissionIds.map(id => this.deleteUserPermission(id)));
  }

1 个答案:

答案 0 :(得分:0)

找到原因: zip()没有参数

就我而言,我正在使用zip来组合创建和删除,请参见:

const createRequests = [c1, c2];
const deleteRequests = [d1, d2];

zip(
  this.service.create(createRequests),
  this.service.delete(deleteRequests),
)....

---
service.ts

create(reqs: CreateRequest[]) {
  return zip(...reqs.map(req => this.createSingle(req));
}

delete(reqs: DeleteRequest[]) {
  return zip(...reqs.map(req => this.deleteSingle(req));
}

但是,如果createRequestsdeleteRequests中的一个为空列表,则此逻辑将出错。例如,如果createRequests为空而deleteRequests不为空,则this.service.delete(deleteRequests)引发的所有HTTP请求将被取消,因为{{1} }。

解决方案:

解决此问题的方法是,我们检查zip()的长度并返回其他可观察到的实例。

固定代码:

this.service.create(createRequests)