从Ember发布到DRF API时406(不可接受)

时间:2016-03-09 07:29:26

标签: ember.js django-rest-framework ember-simple-auth django-cors-headers

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

我怀疑与CORS有关的事情。我在Django中使用django-cors-headers,并在我的开发环境中允许所有。如果重要的话,我还会使用django-rest-framework-jwtdjango-rest-framework-json-api个包。

我的API显示OPTIONS,然后进行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。不幸的是,无论我在Ember做什么都能解决这个问题。我没有尝试为我的应用的JSONAPIAdapter和"Accept": "application/vnd.api+json"设置标题ENV['ember-simple-auth-token']

3 个答案:

答案 0 :(得分:1)

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

// 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文档的Authenticators部分,根据需要选择符合您需求的父身份验证器:ember-simple-auth - Authenticators

答案 1 :(得分:1)

我设法或多或少地解决了这个问题。这是一个令人遗憾的软件包组合,导致在Ember和DRF之间使用JSON API规范时出现了一些问题。

首先,通过简单地将标题作为参数添加到controllers/login.js来覆盖我在.authenticate中设法完成的标题。任何args都会传递给ember-simple-auth身份验证器。 (我不需要实现我自己的身份验证器,正如Pavol在他的回答中所建议的那样。)

// controllers/login.js
import Ember from 'ember';

export default Ember.Controller.extend({
  session: Ember.inject.service('session'),

  actions: {
    authenticate: function() {
      var credentials = this.getProperties('identification', 'password'),
        authenticator = 'authenticator:jwt',
        // Set headers to accept JSON API format
        headers = {
          'Accept': 'application/vnd.api+json',
          'Content-Type': 'application/vnd.api+json'
        };

      this.get('session').authenticate(authenticator, credentials, headers);
    }
  }
});

这引入了下一个问题:我的内容类型实际上并不是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_framework_jwtrest_framework_json_api仍然无法很好地协作。

此时,我认为在auth端点上放弃对JSON API规范的需求要简单得多:Ember的软件包没有产生它,而且DRF在解析它时遇到了麻烦!

所以,我在Ember方面恢复了一切,根据我原来的问题产生了请求。在DRF方面,我将rest_framework_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规范来解决。

答案 2 :(得分:0)

您应该能够在适配器中显式设置内容类型:

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); 
 } 
});

它能解决你的问题吗?