为每个 fetch() 请求设置默认标头

问题描述 投票:0回答:6

是否可以使用 fetch API 为每个请求设置默认标头?
我想要做的是每当

Authorization
中存在 json Web 令牌时就设置一个
localStorage
标头。我当前的解决方案是使用此函数设置标题:

export default function setHeaders(headers) {
    if(localStorage.jwt) {
        return {
            ...headers,
            'Authorization': `Bearer ${localStorage.jwt}`
        }
    } else {
        return headers;
    }
}

在获取请求中设置标头将如下所示:

return fetch('/someurl', {
        method: 'post',
        body: JSON.stringify(data),
        headers: setHeaders({
            'Content-Type': 'application/json'
        })
    })

但是必须有更好的方法来做到这一点。我目前正在开发一个 React/Redux/Express 应用程序,如果有帮助的话。

reactjs express redux authorization fetch-api
6个回答
39
投票

创建一个

fetch
包装器可以解决您的问题:

function updateOptions(options) {
  const update = { ...options };
  if (localStorage.jwt) {
    update.headers = {
      ...update.headers,
      Authorization: `Bearer ${localStorage.jwt}`,
    };
  }
  return update;
}

export default function fetcher(url, options) {
  return fetch(url, updateOptions(options));
}

如果您决定更喜欢 Axios 或其他软件包,您还可以获得额外的好处,即能够轻松地为应用程序中的所有调用切换请求客户端。您还可以执行其他操作,例如检查

options.body
是否是对象并添加
'Content-Type: application/json
标头。


10
投票

Andri Möll 为 fetch 创建了一个

FetchDefaults.js
mixin,用于设置获取默认值:

var Url = require("url")
var assign = require("oolong").assign
var merge = require("oolong").merge
var PARSE_QUERY = false
var PROTOCOL_RELATIVE = true // Enable //example.com/models to mimic browsers.

exports = module.exports = function(fetch, rootUrl, defaults) {
  if (typeof rootUrl === "string") rootUrl = parseUrl(rootUrl)
  else defaults = rootUrl, rootUrl = null
  return assign(exports.fetch.bind(null, fetch, rootUrl, defaults), fetch)
}

exports.fetch = function(fetch, rootUrl, defaults, url, opts) {
  if (rootUrl != null) url = rootUrl.resolve(url)
  if (typeof defaults === "function") defaults = defaults(url, opts)
  return fetch(url, opts == null ? defaults : merge({}, defaults, opts))
}

function parseUrl(url) {
  return Url.parse(url, PARSE_QUERY, PROTOCOL_RELATIVE)
}

根据仅 AGPL-3.0 许可证

分发

9
投票

您可以使用 Axios 代替 fetch,并使用拦截器

const setAuthorization = (token) => {

  api.interceptors.request.use((config) => {
    config.headers.Authorization = 'Bearer ' + token;
    return config;
  });

}

其中 Api 是带有基本 URL 的 axios 对象

const api= axios.create({
  baseURL: 'http://exemple.com'
});

当您获得令牌时,您只需调用函数 setAuthorization 即可。

来源:Axios README.md


5
投票

您可以覆盖默认的 fetch api:

var originalFetch = window.fetch;
window.fetch = function (input, init) {
    if (!init) {
        init = {};
    }
    if (!init.headers) {
        init.headers = new Headers();
    }

    // init.headers could be: 
    //   `A Headers object, an object literal, 
    //    or an array of two-item arrays to set request’s headers.`
    if (init.headers instanceof Headers) {
        init.headers.append('MyHeader', 'Value');
    } else if (init.headers instanceof Array) {
        init.headers.push(['MyHeader', 'Value']);
    } else {
        // object ?
        init.headers['MyHeader'] = 'Value';
    }
    return originalFetch(input, init);
};

参考资料:


2
投票

一个快速但不推荐的技巧是重新定义默认的

.fetch()
函数:

const oldFetch = window.fetch;
window.fetch = function() {
    arguments[1].headers = { 'blahblah' : 'blabla' };
    return oldFetch.apply(window, arguments);
}

代码未经测试且未完成。如果您决定使用这个答案,请检查

arguments.length
,添加代码以保留现有标头等。我只是提供进一步探索的方向。


0
投票

2024年答案

我不建议您覆盖全局 fetch API,如果其他库或服务依赖于 fetch,您可能会遇到问题。

制作一个获取包装器是正确的方法。 下面我假设我们想要保留所有 fetch API 行为。

第一步:创建一个助手来合并标题

因为 fetch API 的标头可以通过以下方式传递:

  • 一个物体
  • 条目数组(例如
    [['key1', 'val1'], ['key2', 'val2']]
  • 一个 header 实例

我们需要一个

mergeHeaders
帮手。 我将传递详细信息,但这是一个有效的实现:

function mergeHeaders (...headerInits) {
   let result = {}
   headerInits.forEach((init) => {
      new Headers(init).forEach((value, key) => {
         if (value === 'null' || value === 'undefined') {
            // same as object spread: undefined overrides the current value
            // 'null' and 'undefined' are not valid headers values
            // therefore in this case we can remove the header
            delete res[key]
         } else {
            // add the header
            res[key] = value
         }
      })
   })
   return result
}

第二步:创建新的fetcher

我们现在可以继续实现一个基本的获取器

function fetcher(input, options) {
    // your headers
    const defaultHeaders = { Authorization: localStorage.getItem('auth-header') }
    // merge them with the headers of the options
    const headers = mergeHeaders(defaultHeaders, options.headers)
    // add the headers to the options
    return fetch(input, { ...options, headers })
}

第三步:使自动完成工作

如果您不使用打字稿,您需要添加一些 jsdocs

jsdocs 是在底层使用 typescript lsp 的类型注释。 他们将启用自动完成

/**
 * @type {typeof fetch}
 */
function fetcher(input, options) {
    // your headers
    const defaultHeaders = { Authorization: localStorage.getItem('auth-header') }
    // merge them with the headers of the options
    const headers = mergeHeaders(defaultHeaders, options.headers)
    // add the headers to the options
    return fetch(input, { ...options, headers })
}

我们开始了,我们有一个带有默认标头的获取器!

如果您的用例开始变得更加复杂,请考虑使用库。 我最近发布了我的 fetch API 配置库,名为 up-fetch

这是一个使用 up-fetch

的快速示例
const fetcher = up(fetch, () => ({
    headers: { Authorization: localStorage.getItem('auth-header') }
}))

结果相同,只是自动解析响应,如果

response.ok
false
,则会抛出错误。 还有更多功能,例如参数作为对象、baseUrl 配置、验证适配器、拦截器...

希望有帮助

© www.soinside.com 2019 - 2024. All rights reserved.