`setRequestHeader` 无法使用源代码作为标头值执行,axios 和 React

Lev*_*kov 5 interceptor http-headers typescript reactjs axios

我遇到了一个非常奇怪的问题,实现 axios 拦截器来处理过期的令牌并刷新它。

\n

环境

\n

我正在使用访问和刷新令牌实施 JWT 身份验证。

\n

当请求发送到需要 JWT 身份验证的 API 路由时,请求拦截器会确保标头包含带有不记名令牌的授权。响应拦截器检查是否需要新的访问令牌,发送刷新请求,最后使用新配置更新 axios 实例。

\n

我按照 Dave Gray 的视频编写了代码,但使用了 TypeScript。

\n

问题

\n

测试此代码时,我将刷新令牌生存期设置为非常长,同时将访问令牌生存期设置为 5 秒。过期后,当对受保护路由的请求发生时,一切都按照计划进行\xe2\x80\x94来自后端的日志包含两个成功完成的请求:(1)到带有 401 响应的受保护路由,然后(2 ) 刷新请求。

\n

此时,我在浏览器控制台(Chrome 和 Safari)中看到 DOMException,它指出setRequestHeader由于源代码函数不是有效的标头值而无法执行。当然,事实并非如此!这段代码是这样的

\n

代码

\n
const axiosPrivate = axios.create({\n  baseURL: BASE_URL,\n  headers: { "Content-Type": "application/json" },\n  withCredentials: true,\n});\n\ninterface IRequestConfig extends AxiosRequestConfig {\n  sent?: boolean;\n}\n\nconst useAxiosPrivate = () => {\n  const { auth } = useAuth()!;\n  const refresh = useRefreshToken();\n\n  React.useEffect(() => {\n    const requestInterceptor = axiosPrivate.interceptors.request.use(\n      (config: AxiosRequestConfig) => {\n        config.headers = config.headers ?? {};\n        if (!config.headers["Authorization"]) {\n          config.headers["Authorization"] = `Bearer ${auth?.token}`;\n        }\n        return config;\n      },\n      async (error: AxiosError): Promise<AxiosError> => {\n        return Promise.reject(error);\n      }\n    );\n\n    const responseInterceptor = axiosPrivate.interceptors.response.use(\n      (response: AxiosResponse) => response,\n      async (error: AxiosError): Promise<AxiosError> => {\n        const prevRequestConfig = error.config as IRequestConfig;\n        if (error?.response?.status === 401 && !prevRequestConfig?.sent) {\n          const newAccessToken = await refresh();\n          prevRequestConfig.sent = true;\n          prevRequestConfig.headers = prevRequestConfig.headers!;\n          prevRequestConfig.headers[\n            "Authorization"\n          ] = `Bearer ${newAccessToken}`;\n          return axiosPrivate(prevRequestConfig);\n        }\n        return Promise.reject(error);\n      }\n    );\n\n    return () => {\n      axiosPrivate.interceptors.request.eject(requestInterceptor);\n      axiosPrivate.interceptors.response.eject(responseInterceptor);\n    };\n  }, [auth, refresh]);\n\n  return axiosPrivate;\n};\n
Run Code Online (Sandbox Code Playgroud)\n

错误

\n
DOMException: Failed to execute \'setRequestHeader\' on \'XMLHttpRequest\': \'function (header, parser) {\n    header = normalizeHeader(header);\n    if (!header) return undefined;\n    const key = findKey(this, header);\n\n    if (key) {\n      const value = this[key];\n\n      if (!parser) {\n        return value;\n      }\n\n      if (parser === true) {\n        return parseTokens(value);\n      }\n\n      if (_utils_js__WEBPACK_IMPORTED_MODULE_0__["default"].isFunction(parser)) {\n        return parser.call(this, value, key);\n      }\n\n      if (_utils_js__WEBPACK_IMPORTED_MODULE_0__["default"].isRegExp(parser)) {\n        return parser.exec(value);\n      }\n\n      throw new TypeError(\'parser must be boolean|regexp|function\');\n    }\n  }\' is not a valid HTTP header field value.\n
Run Code Online (Sandbox Code Playgroud)\n

研究

\n

到目前为止,我只在互联网上 发现了一个类似的问题,其中有其他一些问题的链接。其中之一axios给了我一个提示,这可能是如何读取 axios 实例的配置的问题。

\n

我不确定问题是否确实出在 axios 的某个地方。如果您对这个问题有任何有用的想法,我将非常感激!

\n

Dan*_*Dan 7

我遇到了同样的问题,我通过手动赋予值而axiosPrivate不是 来解决它axiosPrivate(prevRequestConfig)

const responseIntercept = axiosPrivate.interceptors.response.use(
        response => response,
        async (error)=>{
            const prevRequest = error?.config;
            if (error?.response?.status === 403 && !prevRequest?.sent){
                const newAccessToken = await refresh();
                // console.log(prevRequest);
                return axiosPrivate({
                    ...prevRequest,
                    headers: {...prevRequest.headers, Authorization: `Bearer ${newAccessToken}`},
                    sent: true
                });
            }
            return Promise.reject(error);
        }
    );
Run Code Online (Sandbox Code Playgroud)