Ember.js 406(不可接受)当从余烬发布到DRF API时

Ember.js 406(不可接受)当从余烬发布到DRF API时,ember.js,django-rest-framework,ember-simple-auth,django-cors-headers,Ember.js,Django Rest Framework,Ember Simple Auth,Django Cors Headers,我正在使用ember-simple-auth和ember-simple-auth-token允许用户登录我的应用程序。然而,当我在后端通过POST请求调用Django Rest Framework以使用用户名和密码进行身份验证时,我得到一个406(不可接受)错误。这不会发生在DRF可浏览API中,因此后端似乎工作正常 我怀疑与CORS有关。我在django中使用django cors头文件,并允许在我的开发环境中使用所有文件。我还使用了django-rest-frameworkjwt和djang

我正在使用
ember-simple-auth
ember-simple-auth-token
允许用户登录我的应用程序。然而,当我在后端通过POST请求调用Django Rest Framework以使用用户名和密码进行身份验证时,我得到一个406(不可接受)错误。这不会发生在DRF可浏览API中,因此后端似乎工作正常

我怀疑与CORS有关。我在django中使用
django cors头文件
,并允许在我的开发环境中使用所有文件。我还使用了
django-rest-frameworkjwt
django-rest-frameworkjson-api
包,如果这很重要的话

My API显示一个选项,然后显示正在进行的POST调用:

[09/Mar/2016 07:15:54] "OPTIONS /api-token-auth/ HTTP/1.1" 200 0
[09/Mar/2016 07:15:54] "POST /api-token-auth/ HTTP/1.1" 406 114
响应标题:

HTTP/1.0 406 Not Acceptable
Date: Wed, 09 Mar 2016 07:15:54 GMT
Server: WSGIServer/0.2 CPython/3.5.1
X-Frame-Options: SAMEORIGIN
Access-Control-Allow-Origin: *
Content-Type: application/vnd.api+json
Allow: POST, OPTIONS
Vary: Accept
请求标头:

POST /api-token-auth/ HTTP/1.1
Host: localhost:8000
Connection: keep-alive
Content-Length: 2
Accept: application/json, text/javascript
Origin: http://localhost:4200
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/48.0.2564.116 Chrome/48.0.2564.116 Safari/537.36
Content-Type: application/json
Referer: http://localhost:4200/login
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8

请求头不显示
application/vnd.api+json
,而是显示
application/json
。不幸的是,无论我在余烬做什么,都能解决这个问题。对于我的应用程序的JSONAPIAdapter,我尝试将标题设置为
“Accept”:“application/vnd.api+json”
,但未成功,在
ENV['ember-simple-auth-token']
中,您应该能够在适配器中显式设置内容类型:

export default DS.JSONAPIAdapter.extend({ 
 // set content-type upon every ajax request 
 ajax: function(url, type, hash){ 
 hash = hash || {} ;
 hash.headers = hash.headers || {};
 hash.headers['Content-Type'] = 'application/vnd.api+json';
 return this._super(url, type, hash); 
 } 
});

它解决了您的问题吗?

实现您自己的验证器,该验证器设置身份验证请求期间使用的标头:

// your-app/authenticators/your-custom-authenticator.js
import OAuth2PasswordGrant from 'ember-simple-auth/authenticators/oauth2-password-grant';

export default OAuth2PasswordGrant.extend({

  /**
   * your backend authentication endpoint
   * @overrides
   */
  serverTokenEndpoint: `https://your.authentication.endpoint.sth/login`,

  /**
   * Makes a request to the authentication server.
   * This is what you need to override to customize your headers
   * set up for purposes of authentication.
   * @overrides
   */
  makeRequest(url, data) {
    const options = {
      url: url,
      data: data,
      type: 'GET',
      dataType: 'json',
      accept: 'application/vnd.api+json',
      headers: {
        "Content-Type": 'application/vnd.api+json'
      }
    };

    return Ember.$.ajax(options);
  }
});
在您的(登录)路由/控制器/任何需要的地方参考此自定义验证器:

this.get('session').authenticate('authenticator:yourCustomAuthenticator', username, password).then(() => {
          // success, redirect, as you like..
        })

查看ember simple auth docs的Authenticators部分,选择一个尽可能接近您需要的父级验证器:

我或多或少地解决了这个问题。这是一个不幸的包组合,导致在Ember和DRF之间使用JSON API规范时出现一些问题

首先,在我的
controllers/login.js
中,通过简单地将标题作为参数添加到
.authenticate
中,我成功地覆盖了标题。任何参数都会传递到
ember简单身份验证
身份验证程序。(正如帕沃尔在回答中所建议的那样,我不需要实现自己的验证器。)

这引入了下一个问题:我的内容类型实际上不是JSON API规范,因此我确实需要实现自己的验证器来翻译
ember simple auth token
的JWT验证器,以生成JSON API规范兼容的格式。没有让它工作,但像这样:

// authenticators/jwt.js
import Base from 'ember-simple-auth-token/authenticators/token';

export default Base.extend({
  /**
    Returns an object used to be sent for authentication.

    @method getAuthenticateData
    @return {object} An object with properties for authentication.
  */
  // Make sure this is JSON API compatible format.
  getAuthenticateData(credentials) {
    const authentication = {
      // This is apparently not valid JSON API spec, but you get the gist...
      'data': [{
        [this.identificationField]: credentials.identification,
        [this.passwordField]: credentials.password
      }]
    };

    return authentication;
  }
});
现在,在后端,
rest\u framework\u jwt
rest\u framework\u json\u api
仍然不能很好地配合使用

在这一点上,我决定在auth端点上放弃对jsonapi规范的需求要简单得多:Ember的包没有生成它,DRF在解析它时遇到了麻烦

因此,我恢复了余烬端的所有内容,让它根据我原来的问题生成请求。在DRF方面,我对
rest\u framework\u jwt
的视图进行了子类化,并将解析器设置为DRF的默认
JSONParser

"""
Make the JWT Views ignore JSON API package and use standard JSON.
"""

from rest_framework_jwt.views import ObtainJSONWebToken, RefreshJSONWebToken, \
    VerifyJSONWebToken
from rest_framework.parsers import JSONParser
from rest_framework.renderers import JSONRenderer


class ObtainJSONWebTokenPlainJSON(ObtainJSONWebToken):
    parser_classes = (JSONParser, )
    renderer_classes = (JSONRenderer, )


class RefreshJSONWebTokenPlainJSON(RefreshJSONWebToken):
    parser_classes = (JSONParser, )
    renderer_classes = (JSONRenderer,)


class VerifyJSONWebTokenPlainJSON(VerifyJSONWebToken):
    parser_classes = (JSONParser, )
    renderer_classes = (JSONRenderer,)

最终结果:解决方法是让我的API在任何地方都遵循JSON API规范,除了令牌身份验证端点。

我想我听说默认情况下ember和DRF不兼容。您使用的是类似于?@ilse2005的东西吗?我正在使用
django rest framework json api
使DRF响应与json api规范兼容。您能否至少确认您的请求在标题
内容类型
字段中使用
application/vnd.api+json
而不是
application/json
?它没有。如果可以的话,这可能解决了我的问题。我建议在上面的
ajax()
函数中设置一个断点,并澄清程序执行是否达到断点。之后,我可能会转到这个.super()并观察到底从何处获得
内容类型
标题重写如果你是对的,那么它只会在余烬数据请求中达到这一点,而不会在身份验证请求中达到这一点。请看我下面的帖子,它现在应该可以解决你的问题了;)在FWI中,我能够使JSON API和普通JSON请求都工作(从而实现身份验证)只需确保DRF设置的
DEFAULT\u PARSER\u类
中包括
rest\u framework.parsers.JSONParser
rest\u framework\u json\u api.renders.JSONRenderer
rest\u framework.renders.JSONRenderer
,即可
默认渲染器类
setting@TimmyO“玛奥尼,这对我也有用。谢谢,它使我的代码更加简洁。
"""
Make the JWT Views ignore JSON API package and use standard JSON.
"""

from rest_framework_jwt.views import ObtainJSONWebToken, RefreshJSONWebToken, \
    VerifyJSONWebToken
from rest_framework.parsers import JSONParser
from rest_framework.renderers import JSONRenderer


class ObtainJSONWebTokenPlainJSON(ObtainJSONWebToken):
    parser_classes = (JSONParser, )
    renderer_classes = (JSONRenderer, )


class RefreshJSONWebTokenPlainJSON(RefreshJSONWebToken):
    parser_classes = (JSONParser, )
    renderer_classes = (JSONRenderer,)


class VerifyJSONWebTokenPlainJSON(VerifyJSONWebToken):
    parser_classes = (JSONParser, )
    renderer_classes = (JSONRenderer,)