ReactJS管理面板向Expo Go发推送通知遇CORS问题求助
解决React管理面板向Expo Go推送通知的CORS问题
我用ReactJS开发了管理面板,Expo Go开发移动端应用,尝试从管理面板向Expo Go客户端推送通知时遇到CORS错误:Access to fetch at 'https://exp.host/--/api/v2/push/send' from origin 'http://localhost:3000' has been blocked by CORS policy
但使用Expo推送通知工具可正常工作,请问如何实现从React向Expo发送通知?
React代码
const response = await fetch('https://exp.host/--/api/v2/push/send', { method: 'POST', // mode: 'no-cors', headers: { 'accept': 'application/json', 'content-Type': 'application/json', 'accept-encoding': 'gzip, deflate' }, body: JSON.stringify({ to: tokens, title: title, body: body, }), }); console.log(response) if(response.ok){ try { const docRef = await addDoc(collection(db, "broadcast"), { title: title, body:body, tokens:tokens }); console.log("Document written with ID: ", docRef.id); toast.success(`broadcast ${title} sent.`, {position: "top-right",}) } catch (e) { console.error("Error adding document: ", e); toast.error(e, {position: "top-right",}) } }else{ console.error("Error sending broadcast: ", response.status); toast.error(`Error sending broadcast - ${response.status}`, {position: "top-right",}) alert(`Error sending broadcast - ${response.status}`) }
Expo代码
const registerForPushNotifications = async (alertUser) => { console.log('register for push notifications') if (Device.isDevice) { const { status: existingStatus } = await Notifications.getPermissionsAsync(); let finalStatus = existingStatus; if (existingStatus !== 'granted') { const { status } = await Notifications.requestPermissionsAsync(); finalStatus = status; } if (finalStatus !== 'granted') { throw new Error('Failed to get push token for push notification!'); } const token = await Notifications.getExpoPushTokenAsync(); const expotoken = await AsyncStorage.getItem('expopushtoken') if(expotoken===null){ // await expoPushTokensApi.register(token.data); try { const docRef = await addDoc(collection(db, "expoPushToken"), { uid: user.uid, expopushtoken:token.data, timestamp: serverTimestamp() }); console.log("Document written with ID: ", docRef.id); } catch (e) { console.error("Error adding document: ", e); } console.log('expopushtoken doesnt exist',token.data) await AsyncStorage.setItem('expopushtoken', token.data) }else{ console.log('expopushtoken exists',expotoken); //check to see if its saved in database const q = query(collection(db, "expoPushToken"), where("uid", "==", user.uid)); await getDocs(q).then(async(snapshot)=>{ console.log('isEmpty',snapshot.empty); if(snapshot.empty){ try { const docRef = await addDoc(collection(db, "expoPushToken"), { uid: user.uid, expopushtoken:token.data, timestamp: serverTimestamp() }); console.log("Document written with ID: ", docRef.id); } catch (e) { console.error("Error adding document: ", e); } }}) } } else { console.warn('Must use physical device for Push Notifications'); } };
解决方案
核心原因
浏览器的CORS策略阻止了前端直接从http://localhost:3000请求Expo的推送API,因为Expo服务器未配置允许该来源的跨域请求。而Expo官方工具是在服务器端发起请求,不受浏览器CORS限制,所以可以正常工作。
方法一:后端代理请求(生产/开发通用,推荐)
在你的后端服务(如Node.js、Firebase Functions、Python Flask等)中封装一个接口,前端调用自己的后端接口,再由后端去请求Expo的推送API,绕开浏览器的CORS限制。
示例:Node.js + Express后端接口
const express = require('express'); const router = express.Router(); const fetch = require('node-fetch'); router.post('/send-expo-notification', async (req, res) => { try { const { tokens, title, body } = req.body; const expoResponse = await fetch('https://exp.host/--/api/v2/push/send', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Accept-Encoding': 'gzip, deflate' }, body: JSON.stringify({ to: tokens, title: title, body: body }) }); const data = await expoResponse.json(); res.status(expoResponse.status).json(data); } catch (error) { res.status(500).json({ error: error.message }); } }); module.exports = router;
修改后的React前端代码
// 调用自己的后端接口 const response = await fetch('/api/send-expo-notification', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ to: tokens, title: title, body: body }) }); // 后续逻辑保持不变 console.log(response); if(response.ok){ // ... 原有的成功处理逻辑 }else{ // ... 原有的错误处理逻辑 }
方法二:React开发服务器代理(仅开发环境)
如果只是开发阶段临时解决,可以在React项目的package.json中添加代理配置,让开发服务器转发请求:
{ "name": "your-react-app", "version": "0.1.0", "proxy": "https://exp.host", // 添加这一行 // ... 其他配置 }
然后修改React中的请求地址:
const response = await fetch('/--/api/v2/push/send', { method: 'POST', headers: { 'accept': 'application/json', 'content-Type': 'application/json', 'accept-encoding': 'gzip, deflate' }, body: JSON.stringify({ to: tokens, title: title, body: body }) });
注意:此方法仅适用于开发环境,生产环境必须使用后端代理。
额外检查项
- 确保
to字段是Expo推送令牌数组,即使单个令牌也要用数组包裹(如["ExponentPushToken[xxx]"]) - 验证Expo推送令牌的有效性,无效令牌会导致请求失败
- 可根据需求添加Expo通知的可选字段(如
sound、data)丰富通知内容
内容的提问来源于stack exchange,提问作者kd12345
相关产品推荐
相关产品推荐

