我正在开发一个使用Angular + Django(Django Rest Framework)的项目。在开发过程中,CORS支持是通过使用django-cors-headers,CORS_ORIGIN_ALLOW_ALL = True
和CORS_ALLOW_CREDENTIALS = True
来完成的。
当我尝试发送POST请求以在前端(Angular)中创建一些资源时,Chrome会发送一些飞行前OPTIONS请求并由后端服务器(python manage.py runserver)成功响应,但其他请求则不然。由于未知原因,这些请求被取消,后端服务器日志表明服务器已接收并接受请求,详细信息如下图所示。
失败请求的标头如下所示。
但是,如果复制标题的内容并尝试使用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)));
}
找到原因:没有参数的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));
}
但如果其中一个createRequests
和deleteRequests
是一个空列表,那么这个逻辑就会出错。例如,如果createRequests
为空而deleteRequests
不为,则this.service.delete(deleteRequests)
触发的所有HTTP请求都将被取消,因为zip()
返回空this.service.create(createRequests)
。
解:
这个问题的解决方案是我们检查reqs
的长度并返回其他可观察的instaned。
固定代码:
create(reqs: CreateRequest[]) {
if (reqs.length === 0) return of([]);
return zip(...reqs.map(req => this.createSingle(req));
}
delete(reqs: DeleteRequest[]) {
if (reqs.length === 0) return of([]);
return zip(...reqs.map(req => this.deleteSingle(req));
}