在React Native中使用Fetch时如何使用授权头(Authorization Header)

219

我正在尝试在React Native中使用fetch从Product Hunt API获取信息。我已经获得了正确的Access Token并将其保存到状态中,但似乎无法在GET请求的Authorization标头中传递它。

目前我有以下代码:

var Products = React.createClass({
  getInitialState: function() {
    return {
      clientToken: false,
      loaded: false
    }
  },
  componentWillMount: function () {
    fetch(api.token.link, api.token.object)
      .then((response) => response.json())
      .then((responseData) => {
          console.log(responseData);
        this.setState({
          clientToken: responseData.access_token,
        });
      })
      .then(() => {
        this.getPosts();
      })
      .done();
  },
  getPosts: function() {
    var obj = {
      link: 'https://api.producthunt.com/v1/posts',
      object: {
        method: 'GET',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
          'Authorization': 'Bearer ' + this.state.clientToken,
          'Host': 'api.producthunt.com'
        }
      }
    }
    fetch(api.posts.link, obj)
      .then((response) => response.json())
      .then((responseData) => {
        console.log(responseData);
      })
      .done();
  },

我对我的代码期望如下:

  1. 首先,我将使用从我导入的API模块获取的数据fetch访问令牌。
  2. 之后,我将设置this.stateclientToken属性等于所接收到的访问令牌。
  3. 然后,我将运行getPosts,该函数应返回一个包含来自Product Hunt的当前文章数组的响应。

我能够验证访问令牌正在被接收并且this.state正将其作为它的clientToken属性。我还可以验证getPosts正在运行。

我收到的错误信息如下:

{"error":"unauthorized_oauth", "error_description":"Please supply a valid access token. Refer to our api documentation about how to authorize an api request. Please also make sure you require the correct scopes. Eg \"private public\" for to access private endpoints."}

我一直在假设我没有正确地在我的授权标头中传递访问令牌,但似乎无法确定确切原因。


2
正如在此SO中指出的那样,标头应该是小写字母(有些服务器会尊重这一点,而另一些则不会)。 我只是分享一下,因为我自己不知道而被它所困扰(并浪费了时间来调试问题)。 不幸的是,许多项目、示例和文章似乎没有遵守这一点。 - t.j.
@t.j. 头部名称不区分大小写,这正是您链接的问题所述的内容。 - coreyward
1
5.5年后,我像OP一样设置了一个获取头部: 'Authorization': 'Bearer ' + myJWT,结果发现myJWT被双引号包裹了! Authorization: Bearer "yadda.yadda.yadda"经过多次尝试解决,我在后端过滤了双引号。 - GuyWrySmile
5个回答

303

带有授权头的示例获取:

fetch('URL_GOES_HERE', { 
    method: 'post', 
    headers: new Headers({
        'Authorization': 'Basic '+btoa('username:password'), 
        'Content-Type': 'application/x-www-form-urlencoded'
    }), 
    body: 'A=1&B=2'
});

9
对我来说这不起作用。根据Firebug,'Authorization'头部信息默默地未能附加上。我尝试了在可选对象中包含credentials: 'include',但仍然无效。 - Ronnie Royston
8
你是否在关注 OPTIONS 请求?如果 API 端点没有启用 CORS(即从不同域名访问时的 Access-Control-Allow-Origin: *),那么它可能会在 OPTIONS 请求时失败。 - Cody Moniz
1
API端点没有启用CORS,这可能是为什么它对我不起作用的原因。谢谢。最终我安装了Firefox的“跨域资源共享(CORS)”插件,它可以正常工作。 - Ronnie Royston
4
关于@RonRoyston所看到的问题,你需要导入btoa库,这不是node的本地库(它是一个浏览器库的移植版)。否则,认证头创建会静默失败。我们也遇到了同样的问题。 - Freewalker
3
根据文档,需要使用new Headers()来包装头信息。 - Daniel Dror
显示剩余8条评论

99

事实证明我一直在错误地使用fetch方法。

fetch接受两个参数:API的端点和一个可选对象,其中可以包含正文和标头。

我将预期的对象包裹在第二个对象中,这样不会得到任何想要的结果。

以下是高层次的代码示例:

    fetch('API_ENDPOINT', options)  
      .then(function(res) {
        return res.json();
       })
      .then(function(resJson) {
        return resJson;
       })

我将我的选项对象结构化如下:

    var options = {  
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'Origin': '',
        'Host': 'api.producthunt.com'
      },
      body: JSON.stringify({
        'client_id': '(API KEY)',
        'client_secret': '(API SECRET)',
        'grant_type': 'client_credentials'
      })
    }

你能提供现在可用的代码吗?我正在尝试使用带有授权头的fetch,但我认为我的授权代码没有作为头传递,因为我收到了401响应。 - MikaelC
2
完成,希望有所帮助。 - Richard Kho
1
哦,我曾经在你的个人网站上看到过那个示例!这就是我第一次建模的方式。不过,我已经找出问题所在了,只是我的URL写错了。需要在末尾添加一个斜杠“/”,而我漏掉了这个… - MikaelC
1
谢谢,这很有帮助。值得注意的是,虽然fetch文档指出fetch不处理cookies,但您也可以使用此代码手动将cookies添加到标头中。只需保存uid和key,然后执行以下操作:var obj = { method: 'GET', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Cookie': 'uid='+uid+'; key='+key }); - Dustin
@RichardKho,您能否帮我检查一下这里的帖子。直到现在都没有得到答案... https://dev59.com/otL7oIgBc1ULPQZFqHZa - 3awny

28

如果您使用Bearer令牌,则以下代码段应该有效:

const token = localStorage.getItem('token')

const response = await fetch(apiURL, {
        method: 'POST',
        headers: {
            'Content-type': 'application/json',
            'Authorization': `Bearer ${token}`, // notice the Bearer before your token
        },
        body: JSON.stringify(yourNewData)
    })

15

我曾经遇到过完全相同的问题,我使用django-rest-knox进行身份验证令牌。事实证明,我的获取方法看起来像这样,并没有任何错误:

...
    let headers = {"Content-Type": "application/json"};
    if (token) {
      headers["Authorization"] = `Token ${token}`;
    }
    return fetch("/api/instruments/", {headers,})
      .then(res => {
...

我曾经在运行Apache。

对我解决这个问题的方法是,将wsgi.conf中的WSGIPassAuthorization更改为'On'

我在AWS EC2上部署了一个Django应用程序,并使用Elastic Beanstalk来管理我的应用程序,所以在django.config中,我进行了以下操作:

container_commands:
  01wsgipass:
    command: 'echo "WSGIPassAuthorization On" >> ../wsgi.conf'

django.config 是在项目中还是在 Apache 配置文件中? - perymerdeka

-7
completed = (id) => {
    var details = {
        'id': id,

    };

    var formBody = [];
    for (var property in details) {
        var encodedKey = encodeURIComponent(property);
        var encodedValue = encodeURIComponent(details[property]);
        formBody.push(encodedKey + "=" + encodedValue);
    }
    formBody = formBody.join("&");

    fetch(markcompleted, {
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: formBody
    })
        .then((response) => response.json())
        .then((responseJson) => {
            console.log(responseJson, 'res JSON');
            if (responseJson.status == "success") {
                console.log(this.state);
                alert("your todolist is completed!!");
            }
        })
        .catch((error) => {
            console.error(error);
        });
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接