React Native无法连接Firebase Cloud Functions API求助
React Native调用Firebase Cloud Functions API返回Axios Network Error
环境与问题概述
- 技术栈:React Native 0.73 + Node Express + Firebase Firestore(免费版)
- 单独测试正常:API通过Postman/浏览器可正常访问,React Native应用在外部设备可运行
- 异常情况:调用公共API(如jsonplaceholder)正常,但调用本地部署的Firebase Cloud Functions API时返回
Axios Error: Network Error - 已尝试:关闭防火墙、修改端口,问题未解决;
npx react-native run-android报错,目前用npm start运行应用;Firebase配置遵循官方文档
API代码(Firebase Cloud Functions)
const functions = require("firebase-functions"); const admin = require("firebase-admin"); const express = require("express"); const cors = require("cors"); const app = express(); const permisos = require("./permisos.json"); admin.initializeApp({ credential: admin.credential.cert(permisos), databaseURL: "https://url-que-te-da-firestore.firebaseio.com", }); const db = admin.firestore(); app.use(cors({origin: true})); exports.app = functions.https.onRequest(app); /** * Genera un objeto de respuesta con un formato específico. * * @param {string} codigo - El código de estado de la respuesta. * @param {any} entrada - La entrada o resultado de la operación. * @return {Object} Objeto de respuesta con un formato específico. */ function salida(codigo, entrada) { const today = new Date(); const date = today.getFullYear()+"-"+ (today.getMonth()+1)+"-"+ today.getDate()+"|"+ today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds(); if (codigo === "200") { return { mensaje: "Proceso terminado exitosamente", folio: date, resultado: entrada, }; } if (codigo === "201") { return { mensaje: "Elemento creado exitosamente", folio: date, resultado: entrada, }; } if (codigo === "500") { return { mensaje: "Ocurrio un detalle en el servidor", folio: date, resultado: entrada, }; } return { mensaje: "Ocurrio un detalle en el servidor", folio: date, resultado: entrada, }; } // Añadir libros app.post("/books", (req, res) => { (async () => { try { await db.collection("books").doc("/" + req.body.id + "/") .create({titulo: req.body.titulo}); return res.status(200).send(salida("200", "Libro creado correctamente")); } catch (error) { console.log(error); return res.status(500).send(salida("500", error)); } })(); }); // Leer todos los libros app.get("/books", async (req, res) => { try { const query = db.collection("books"); const querySnapshot = await query.get(); const docs = querySnapshot.docs; const response = docs.map((doc) => ({ id: doc.id, titulo: doc.data().titulo, })); return res.status(200).json(salida("200", response)); } catch (error) { return res.status(500).json(salida("500", error)); } }); // Leer solo 1 libro por id app.get("/books/:id", (req, res) =>{ (async () => { try { const doc = db.collection("books").doc(req.params.id); const item = await doc.get(); const response = item.data(); return res.status(200).json(salida("200", response)); } catch (error) { return res.status(500).send(salida("500", error)); } })(); }); // Modificar libros app.put("/books/:id", async (req, res) => { try { const document = db.collection("books").doc(req.params.id); await document.update({ titulo: req.body.titulo, }); return res.status(200).json(salida("200", "El libro ha sido actualizado")); } catch (error) { return res.status(500).json(salida("500", error)); } }); // Borrar libros app.delete("/books/:id", async (req, res) => { try { const doc = db.collection("books").doc(req.params.id); await doc.delete(); return res.status(200).json(salida("200", "El libros ha sido borrado")); } catch (error) { return res.status(500).send(salida("500", error)); } });
React Native应用代码
import React, { useEffect, useState } from 'react'; import type { FC, ReactNode } from 'react'; import { SafeAreaView, ScrollView, StatusBar, StyleSheet, Text, useColorScheme, View, } from 'react-native'; import axios from 'axios'; type SectionProps = { title: string; children?: ReactNode; }; const Section: FC<SectionProps> = ({ children, title }) => { const isDarkMode = useColorScheme() === 'dark'; return ( <View style={styles.sectionContainer}> <Text style={[ styles.sectionTitle, { color: isDarkMode ? 'white' : 'black', }, ]}> {title} </Text> <Text style={[ styles.sectionDescription, { color: isDarkMode ? 'light' : 'dark', }, ]}> {children} </Text> </View> ); }; const App: FC = () => { const isDarkMode = useColorScheme() === 'dark'; const [dataFromServer, setDataFromServer] = useState<any>(null); useEffect(() => { // Realizar solicitud HTTP al servidor Node.js al cargar la aplicación //http://192.168.1.38:5000/libros-brais/us-central1/app/books axios.get("http://192.168.1.38:5000/libros-brais/us-central1/app/books") .then(response => { setDataFromServer(response.data); }) .catch(error => { console.error(error); }); }, []); const backgroundStyle = { backgroundColor: isDarkMode ? 'darker' : 'lighter', }; return ( <SafeAreaView style={backgroundStyle}> <StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} backgroundColor={backgroundStyle.backgroundColor} /> <ScrollView contentInsetAdjustmentBehavior="automatic" style={backgroundStyle}> {/* ... Otros componentes ... */} <View style={{ backgroundColor: isDarkMode ? 'black' : 'white', }}> <Section title="Step One"> Edit <Text style={styles.highlight}>App.tsx</Text> to change this screen and then come back to see your edits. </Section> <Section title="Data from Server"> <Text>{dataFromServer ? JSON.stringify(dataFromServer) : 'Loading...'}</Text> </Section> {/* ... Otras secciones ... */} </View> </ScrollView> </SafeAreaView> ); }; const styles = StyleSheet.create({ sectionContainer: { marginTop: 32, paddingHorizontal: 24, }, sectionTitle: { fontSize: 24, fontWeight: '600', }, sectionDescription: { marginTop: 8, fontSize: 18, fontWeight: '400', }, highlight: { fontWeight: '700', }, }); export default App;
Firebase Serve运行日志
Serving from 'C:\Users\r3mus\OneDrive\Escritorio\Proyectos\prueba brais react\prueba-brais'... i functions: Watching "C:\Users\r3mus\OneDrive\Escritorio\Proyectos\prueba brais react\prueba-brais\functions" for Cloud Functions... + functions: Using node@18 from host. Serving at port 8292 + functions: Loaded functions definitions from source: app. + functions[us-central1-app]: http function initialized (http://localhost:5000/libros-brais/us-central1/app). ! functions: The Cloud Firestore emulator is not running, so calls to Firestore will affect production.
解决方案
1. 修正API请求端口
从日志可知,Firebase Serve的实际监听端口是8292,将React Native中的请求URL改为:
axios.get("http://192.168.1.38:8292/libros-brais/us-central1/app/books")
2. 确保设备与开发机网络连通
- 确认外部设备和开发机处于同一WiFi网络
- 用设备浏览器直接访问上述修正后的URL,验证是否能正常获取数据,排查网络连通问题
3. 处理Android HTTP限制
Android 9及以上默认禁止HTTP明文请求,需在android/app/src/main/AndroidManifest.xml的<application>标签中添加配置:
<application ... android:usesCleartextTraffic="true">
4. 强化CORS配置
将CORS规则明确为允许React Native调试相关地址:
app.use(cors({ origin: ['http://localhost:8081', 'exp://*', 'http://192.168.1.38:*'], credentials: true }));
5. 排查Functions本地部署问题
- 单独启动Functions服务:
firebase serve --only functions,确认地址正确性 - 查看Functions日志:
firebase functions:log,检查是否有请求到达但处理出错的情况
内容的提问来源于stack exchange,提问作者r3mus99
相关产品推荐
相关产品推荐

