如何在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
相关产品推荐
相关产品推荐

