【发布时间】:2017-08-11 18:02:02
【问题描述】:
我的工作环境是:
-
django==1.10 django-rest-framework==3.5.3-
djangorestframework-jsonapi==2.1.1 -
channels(最新) -
daphne(最新)而不是gunicorn。
我在 docker 环境中使用nginx 作为daphne 上方的代理服务器。
我正在构建一个单独的angular 2 SPA,它连接到上述后端和
我正在使用django-cors-headers==2.0.2 来允许来自该网络应用程序的连接。
适用于:USE_I18N = False
当我设置 Django 的USE_I18N = False 时它工作正常。当尝试对后端进行身份验证时,我发送了一个相当于:
curl -H "Content-Type: application/vnd.api+json" -X POST -d '{"data": {"type": "obtainJSONWebTokens", "attributes": {"email":"admin@email.com", "password":"password"}}}' http://localhost/api/auth/login/ --verbose
卷曲的输出:
* Trying ::1...
* Connected to localhost (::1) port 80 (#0)
> POST /api/auth/login/ HTTP/1.1
> Host: localhost
> User-Agent: curl/7.49.0
> Accept: */*
> Content-Type: application/vnd.api+json
> Content-Length: 107
>
* upload completely sent off: 107 out of 107 bytes
< HTTP/1.1 200 OK
< Server: nginx/1.11.9
< Date: Mon, 20 Mar 2017 13:00:47 GMT
< Content-Type: application/vnd.api+json
< Transfer-Encoding: chunked
< Connection: keep-alive
< Allow: POST, OPTIONS
< X-Frame-Options: SAMEORIGIN
< Content-Language: en
< Vary: Accept, Accept-Language, Cookie
<
{"data":{"token":"<token>"}}
* Connection #0 to host localhost left intact
我收到了我应该收到的 JWT 令牌。一切正常。
失败并显示:USE_I18N = True
但是,USE_I18N = True 时同样的连接失败。
卷曲的输出:
* Trying ::1...
* Connected to localhost (::1) port 80 (#0)
> POST /api/auth/login/ HTTP/1.1
> Host: localhost
> User-Agent: curl/7.49.0
> Accept: */*
> Content-Type: application/vnd.api+json
> Content-Length: 107
* upload completely sent off: 107 out of 107 bytes
< HTTP/1.1 302 Found
< Server: nginx/1.11.9
< Date: Mon, 20 Mar 2017 12:53:49 GMT
< Content-Type: text/html; charset=utf-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< Location: /en/api/auth/login/
< Vary: Cookie
<
* Connection #0 to host localhost left intact
客户端返回的错误是:
XMLHttpRequest cannot load http://localhost/api/auth/login/. Redirect from 'http://localhost/api/auth/login/' to 'http://localhost/en/api/auth/login/' has been blocked by CORS policy: Request requires preflight, which is disallowed to follow cross-origin redirect.
相关设置:
INSTALLED_APPS += (
'corsheaders',
)
if DEBUG is True:
CORS_ORIGIN_ALLOW_ALL = True
MIDDLEWARE_CLASSES = (
'corsheaders.middleware.CorsMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'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',
'django.middleware.security.SecurityMiddleware',
'django.contrib.admindocs.middleware.XViewMiddleware',
)
似乎不是客户端请求失败,而是从“http://localhost/api/auth/login/”重定向到“http://localhost/en/api/auth/login/”,Django 将“en”添加到 URL。
有人能解释一下吗?
我搜索了django-cors-headers 相关问题,但没有一个是针对这种与 I18N 明显不兼容的问题。该库在没有 I18N 的情况下也能正常工作,只是没有打开它。
编辑 2017-03-21
鉴于接受的答案中所述的限制,我选择简单地避免 Django 的语言 URL 重定向。在使用USE_I18N = True 时,我完全避免在根URLconf 中使用i18n_patterns。
事实上,Django Rest Framework 声明这是 API 客户端的最佳实践:
如果您想允许每个请求的语言首选项,您需要在您的
MIDDLEWARE_CLASSES设置中包含django.middleware.locale.LocaleMiddleware。您可以在 Django 文档中找到有关如何确定语言首选项的更多信息。供参考,方法是:
- 首先,它在请求的 URL 中查找语言前缀。
- 如果失败,它会在当前用户的会话中查找
LANGUAGE_SESSION_KEY键。- 如果失败,它会寻找 cookie。
- 如果失败,它会查看
Accept-LanguageHTTP 标头。- 否则,它将使用全局
LANGUAGE_CODE设置。对于 API 客户端,最合适的通常是使用
Accept-Language标头;除非使用会话身份验证,否则会话和 cookie 将不可用,通常更好的做法是更喜欢 API 客户端的Accept-Language标头,而不是使用语言 URL 前缀。
所以,我保持上述设置不变,但在根 URLconf 中更改了以下设置:
urlpatterns += i18n_patterns(
url(_(r'^api/$'), SwaggerSchemaView.as_view(), name='api'),
url(_(r'^api/account/'), include(account_patterns, namespace='account')),
url(_(r'^api/auth/'), include(auth_patterns, namespace='auth')),
url(_(r'^api/'), include('apps.party.api.urls', namespace='parties')),
url(_(r'^api/'), include('apps.i18n.api.urls', namespace='i18n')),
url(_(r'^api-auth/'), include('rest_framework.urls', namespace='rest_framework')),
url(_(r'^admin/'), include(admin_patterns)),
url(_(r'^docs/'), include('apps.docs.urls'))
)
到
urlpatterns += ([
url(_(r'^api/$'), SwaggerSchemaView.as_view(), name='api'),
url(_(r'^api/account/'), include(account_patterns, namespace='account')),
url(_(r'^api/auth/'), include(auth_patterns, namespace='auth')),
url(_(r'^api/'), include('apps.party.api.urls', namespace='parties')),
url(_(r'^api/'), include('apps.i18n.api.urls', namespace='i18n')),
url(_(r'^api-auth/'), include('rest_framework.urls', namespace='rest_framework')),
url(_(r'^admin/'), include(admin_patterns)),
url(_(r'^docs/'), include('apps.docs.urls'))]
)
所以,现在,做:
curl -H "Content-Type: application/vnd.api+json" -H "Accept-Language: pt" -X POST -d '{"data": {"type": "obtainJSONWebTokens", "attributes": {"email":"admin@email.com", "password":"password"}}}' http://localhost:8000/api/auth/login/ --verbose
以请求的语言返回预期的响应(请注意在上面的请求中包含"Accept-Language: pt"):
* Trying ::1...
* Connected to localhost (::1) port 8000 (#0)
> POST /api/auth/login/ HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.49.0
> Accept: */*
> Content-Type: application/vnd.api+json
> Accept-Language: pt
> Content-Length: 107
>
* upload completely sent off: 107 out of 107 bytes
< HTTP/1.1 200 OK
< Transfer-Encoding: chunked
< Allow: POST, OPTIONS
< X-Frame-Options: SAMEORIGIN
< Vary: Accept, Accept-Language, Cookie
< Content-Language: pt
< Content-Type: application/vnd.api+json
<
{"data": {"token":"<token>"}}
* Connection #0 to host localhost left intact
【问题讨论】:
标签: django channels django-cors-headers daphne