reactjs 'setRequestHeader'无法将源代码作为头值、axios和react执行

cuxqih21  于 2022-11-04  发布在  React
关注(0)|答案(2)|浏览(250)

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

设置

我将使用访问和刷新标记实现JWT身份验证。
当请求被发送到需要JWT身份验证的API路由时,请求拦截器在这里确保头部包含Authorization with Bearer令牌。响应拦截器检查是否需要新的访问令牌,发送一个请求以刷新它,最后用新的配置更新axios示例。
我按照Dave Gray的video编写了代码,但使用了TypeScript。
问题
在测试这段代码时,我将刷新令牌的生存期设置得很长,而将访问令牌的生存期设置为5秒。在它过期后,当向受保护的路由发出请求时,一切都按照计划进行-来自后端的日志包含两个成功完成的请求:(1)使用401响应发送到受保护路由,然后(2)发送刷新请求。
此时,我在浏览器控制台(Chrome和Safari)中看到了DOMException,它声明setRequestHeader无法执行,因为源代码函数不是有效的头值。当然,它不是!代码如下。

代码

const axiosPrivate = axios.create({
  baseURL: BASE_URL,
  headers: { "Content-Type": "application/json" },
  withCredentials: true,
});

interface IRequestConfig extends AxiosRequestConfig {
  sent?: boolean;
}

const useAxiosPrivate = () => {
  const { auth } = useAuth()!;
  const refresh = useRefreshToken();

  React.useEffect(() => {
    const requestInterceptor = axiosPrivate.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        config.headers = config.headers ?? {};
        if (!config.headers["Authorization"]) {
          config.headers["Authorization"] = `Bearer ${auth?.token}`;
        }
        return config;
      },
      async (error: AxiosError): Promise<AxiosError> => {
        return Promise.reject(error);
      }
    );

    const responseInterceptor = axiosPrivate.interceptors.response.use(
      (response: AxiosResponse) => response,
      async (error: AxiosError): Promise<AxiosError> => {
        const prevRequestConfig = error.config as IRequestConfig;
        if (error?.response?.status === 401 && !prevRequestConfig?.sent) {
          const newAccessToken = await refresh();
          prevRequestConfig.sent = true;
          prevRequestConfig.headers = prevRequestConfig.headers!;
          prevRequestConfig.headers[
            "Authorization"
          ] = `Bearer ${newAccessToken}`;
          return axiosPrivate(prevRequestConfig);
        }
        return Promise.reject(error);
      }
    );

    return () => {
      axiosPrivate.interceptors.request.eject(requestInterceptor);
      axiosPrivate.interceptors.response.eject(responseInterceptor);
    };
  }, [auth, refresh]);

  return axiosPrivate;
};

错误

DOMException: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': 'function (header, parser) {
    header = normalizeHeader(header);
    if (!header) return undefined;
    const key = findKey(this, header);

    if (key) {
      const value = this[key];

      if (!parser) {
        return value;
      }

      if (parser === true) {
        return parseTokens(value);
      }

      if (_utils_js__WEBPACK_IMPORTED_MODULE_0__["default"].isFunction(parser)) {
        return parser.call(this, value, key);
      }

      if (_utils_js__WEBPACK_IMPORTED_MODULE_0__["default"].isRegExp(parser)) {
        return parser.exec(value);
      }

      throw new TypeError('parser must be boolean|regexp|function');
    }
  }' is not a valid HTTP header field value.

研究
到目前为止,我在互联网上只找到了一个similar issue,它有一些其他的链接。One of them给了我一个提示,它可能是axios如何读取给axios示例的配置的问题。
我不确定这个问题是否真的存在于axios中。我将非常感谢任何关于这个问题的有用想法!

ztmd8pv5

ztmd8pv51#

我遇到了同样的问题,我通过手动为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);
        }
    );
a8jjtwal

a8jjtwal2#

多亏了Vampire Lestat's solution,我才能修改Dave的教程代码:

const responseInterceptor = axiosPrivate.interceptors.response.use(
      (response: AxiosResponse) => {
        return response;
      },
      async (error: AxiosError): Promise<AxiosError> => {
        const prevRequestConfig = error.config as AxiosRequestConfig;
        if (error?.response?.status === 401 && !prevRequestConfig.sent) {
          prevRequestConfig.sent = true;
          const newAccessToken = await refresh();

          /* --- The modified line --- */
          prevRequestConfig.headers = { ...prevRequestConfig.headers };
          /* ------------------------- */

          prevRequestConfig.headers[
            "Authorization"
          ] = `Bearer ${newAccessToken}`;
          return axiosPrivate(prevRequestConfig);
        }
        return Promise.reject(error);
      }
    );

相关问题