当i18n开启

时间:2017-03-20 12:44:11

标签: django channels django-cors-headers daphne

我有一个工作环境:

  • django==1.10
  • django-rest-framework==3.5.3
  • djangorestframework-jsonapi==2.1.1
  • channels(最新)
  • daphne(最新)代替gunicorn

我在nginx上使用daphne作为代理服务器,位于docker环境中。

我正在建立一个独立的angular 2 SPA,连接到上面的后端和 我正在使用django-cors-headers==2.0.2来允许来自该网络应用的连接。

适用于:USE_I18N = False

当我设置Django的USE_I18N = False时,它工作正常。尝试对后端进行身份验证时,我发送的POST请求等同于:

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客户端的最佳实践:

  

如果您想要允许按请求语言偏好设置,则需要在django.middleware.locale.LocaleMiddleware设置中加入MIDDLEWARE_CLASSES

     

您可以在Django文档中找到有关如何确定语言首选项的更多信息。作为参考,方法是:

     
      
  • 首先,它在请求的URL中查找语言前缀。
  •   
  • 如果失败,它会在当前用户的会话中查找LANGUAGE_SESSION_KEY密钥。
  •   
  • 如果没有,它会查找一个cookie。
  •   
  • 如果失败,它会查看Accept-Language HTTP标头。
  •   
  • 如果失败,则使用全局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

2 个答案:

答案 0 :(得分:0)

从本质上讲,您在旧版CORS标准中遇到了错误。

原始标准基本上使得在使用预先请求时无法进行本地重定向。请参阅主题上的this question以及获取标准上的this bug report

在您的情况下,USE_I18N = True会发生这种情况,因为该设置会触发重定向。

希望修补程序很快就会被浏览器实现。 (根据Fetch bug上的latest report,它已经在Edge中运行了。)与此同时,this answer提出了一些解决方法。

答案 1 :(得分:0)

我遇到了同样的问题,因为我没有将BEUS_EnergyDriveAvgDist用于我的所有网址,其中一个不在i18n_patterns内的网址返回了404响应。 我通过覆盖默认导入Django的Middleware LocaleMiddleware解决了这个问题。

i18n_patterns