You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React中通过Fetch获取Rails API(Devise)的响应头令牌

解决React Fetch无法获取Devise Token Auth响应头令牌的问题

方案一:正确配置CORS并获取响应头

问题核心是跨域请求中,自定义响应头默认不会暴露给前端,需要先在Rails后端配置CORS允许暴露这些令牌头,再修改前端Fetch代码提取头信息。

1. Rails后端配置CORS

修改config/initializers/cors.rb,添加expose选项指定要暴露的自定义响应头:

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'http://localhost:3000' # 替换为你的React前端地址
    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head],
      expose: ['access-token', 'client', 'uid', 'expiry'] # 暴露Devise返回的令牌头
  end
end

2. 前端Fetch代码修改

不要直接将响应转为JSON后丢弃原始响应对象,先从res.headers提取令牌:

const url = `${process.env.REACT_APP_API_URL}/auth/sign_in`;

const requestOptions = {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(form)
};

const res = await fetch(url, requestOptions);
// 提取响应头中的令牌
const accessToken = res.headers.get('access-token');
const client = res.headers.get('client');
const uid = res.headers.get('uid');

const data = await res.json();

if(data.errors){
  for(let i = 0; i < data.errors.length; i++){
    setFormErrors(form_errors => [...form_errors, data.errors[i]]);
  }
}else{
  console.log('令牌信息:', accessToken, client, uid);
  // 将令牌保存到localStorage或状态管理工具
  localStorage.setItem('accessToken', accessToken);
  localStorage.setItem('client', client);
  localStorage.setItem('uid', uid);
  // window.location.replace('http://localhost:3002/');
}

方案二:修改Devise将令牌返回在响应体

通过重写Devise的会话控制器,把令牌信息添加到响应体中。

1. 自定义Sessions控制器

创建app/controllers/auth/sessions_controller.rb,继承DeviseTokenAuth的控制器并修改create动作:

class Auth::SessionsController < DeviseTokenAuth::SessionsController
  def create
    super do |resource|
      # 将响应头中的令牌合并到响应体数据中
      @data = @data.merge({
        access_token: response.headers['access-token'],
        client: response.headers['client'],
        uid: response.headers['uid'],
        expiry: response.headers['expiry']
      })
    end
  end
end

2. 更新路由配置

修改config/routes.rb,让Devise路由指向自定义控制器:

Rails.application.routes.draw do
  mount_devise_token_auth_for 'User', at: 'auth', controllers: {
    sessions: 'auth/sessions'
  }
end

3. 前端直接从响应体获取令牌

此时可以直接从返回的JSON数据中提取令牌:

const res = await fetch(url, requestOptions).then(res => res.json());

if(res.errors){
  for(let i = 0; i < res.errors.length; i++){
    setFormErrors(form_errors => [...form_errors, res.errors[i]]);
  }
}else{
  const { access_token, client, uid } = res;
  console.log('令牌信息:', access_token, client, uid);
  // 保存令牌
}

方案对比与推荐

优先选择方案一,理由如下:

  • 遵循Devise Token Auth的官方设计规范,无需修改核心控制器逻辑,降低后续版本升级的兼容性风险。
  • 响应头传递身份令牌是REST API的通用实践,符合HTTP协议设计,响应体可专注于业务数据返回。
  • 方案二需要自定义控制器,增加了代码维护成本,若后续Devise更新,可能需要同步调整自定义逻辑。

内容的提问来源于stack exchange,提问作者Diogo Amaral

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 12:35:27