Angular+Firebase推送通知:Web正常Android无法触发求助
问题:Android设备无法触发通知,但Web端运行正常
我有一个发送通知的callBackend()函数,在Web浏览器中运行完全正常,通知可顺利发送。但在Android设备上运行相同代码时,通知无法触发。
错误信息:
Error sending notification: {"headers":{"normalizedNames":{},"lazyUpdate":null,"headers":{}},"status":0,"statusText":"Unknown Error","url":"http://localhost:3000/notify","ok":false,"name":"HttpErrorResponse","message":"Http failure response for http://localhost:3000/notify: 0 Unknown Error","error":{"isTrusted":true}}
相关代码文件
home.page.ts
import { Component } from "@angular/core"; import { FirebaseMessaging, GetTokenOptions, } from "@capacitor-firebase/messaging"; import { Capacitor } from "@capacitor/core"; import { IonicModule } from "@ionic/angular"; import { environment } from "src/environments/environment"; import { NotificationService } from '../services/notification.service'; @Component({ selector: "app-home", templateUrl: "home.page.html", styleUrls: ["home.page.scss"], standalone: true, imports: [IonicModule], }) export class HomePage { public token = ""; constructor( private notificationService: NotificationService ) { FirebaseMessaging.addListener("notificationReceived", (event) => { console.log("notificationReceived: ", { event }); }); FirebaseMessaging.addListener("notificationActionPerformed", (event) => { console.log("notificationActionPerformed: ", { event }); }); if (Capacitor.getPlatform() === "web") { navigator.serviceWorker.addEventListener("message", (event: any) => { console.log("serviceWorker message: ", { event }); const notification = new Notification(event.data.notification.title, { body: event.data.notification.body, }); notification.onclick = (event) => { console.log("notification clicked: ", { event }); }; }); } } public async requestPermissions(): Promise<void> { await FirebaseMessaging.requestPermissions(); } public async getToken(): Promise<void> { const options: GetTokenOptions = { vapidKey: environment.firebase.vapidKey, }; if (Capacitor.getPlatform() === "web") { options.serviceWorkerRegistration = await navigator.serviceWorker.register("firebase-messaging-sw.js"); } const { token } = await FirebaseMessaging.getToken(options); this.token = token; } public async callBackend() { console.log("call backend", this.token); // Ensure you have the token before making the request if (!this.token) { console.error("Token not available. Make sure you call getToken() first."); return; } const notificationData = { token: this.token, // Include the token in the data notification: { title: 'Test Notification', body: 'This is a test notification' }, data: { detailsId: '123' } }; // Send the request to the backend this.notificationService.sendNotification(notificationData).subscribe( (response) => { console.log('Notification sent successfully:', response); }, (error) => { console.error('Error sending notification:', error); } ); } }
index.js
import express from "express"; import request from "request"; import { google } from "googleapis"; import { readFile } from "fs/promises"; import cors from 'cors'; const app = express(); const port = 3000; // Use the CORS middleware app.use(cors()); app.use(express.json()); // Function to get Google Cloud access token async function getAccessToken() { try { const key = JSON.parse(await readFile(new URL('./angular-firebase-app-b7d17-firebase-adminsdk-3mx51-041a7cc2e5.json', import.meta.url))); const SCOPES = ['https://www.googleapis.com/auth/cloud-platform']; const jwtClient = new google.auth.JWT( key.client_email, null, key.private_key, SCOPES, null ); const tokens = await jwtClient.authorize(); console.log('Access Token:', tokens.access_token); return tokens.access_token; } catch (error) { console.error('Error retrieving access token:', error); throw new Error('Failed to retrieve access token'); } } // Route to send push notification app.post('/notify', async (req, res) => { const { token, notification, data } = req.body; if (!token) { return res.status(400).json({ error: 'Token is required' }); } console.log('Notification Data:', notification, data); console.log('Token:', token); try { const accessToken = await getAccessToken(); // Get access token // Send FCM notification const options = { method: 'POST', url: 'https://fcm.googleapis.com/v1/projects/AppId/messages:send', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}`, // Using the Google Cloud access token }, body: JSON.stringify({ message: { token: token, notification: { title: notification.title, body: notification.body, }, data: data, }, }), }; request(options, (error, response, body) => { if (error) { console.error('Error sending push:', error); return res.status(500).json({ error: 'Failed to send notification' }); } console.log('Push sent successfully:', body); res.json({ success: true, message: 'Notification sent!' }); }); } catch (error) { console.error('Error sending notification:', error); res.status(500).json({ error: 'Internal Server Error' }); } }); // Start server app.listen(port, () => { console.log(`Backend running at http://localhost:${port}`); });
notification.service.ts
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root', }) export class NotificationService { private apiUrl = 'http://localhost:3000/notify'; constructor(private http: HttpClient) {} sendNotification(notificationData: any): Observable<any> { return this.http.post(this.apiUrl, notificationData); } }
解决方案
核心原因
错误里的localhost是关键问题:Web端运行时,浏览器和后端在同一台机器,localhost能正确指向后端服务;但Android设备(模拟器/真机)是独立的设备,localhost指向设备自身,无法访问电脑上的后端服务。
1. 替换localhost为电脑局域网IP
- 查找电脑的局域网IP:Windows用
ipconfig,Mac/Linux用ifconfig或ip addr,格式通常为192.168.x.x - 修改
notification.service.ts中的apiUrl:
private apiUrl = 'http://192.168.x.x:3000/notify'; // 替换为你的局域网IP
2. Android模拟器特殊处理
如果用Android模拟器,可直接用10.0.2.2(模拟器专属映射地址,指向主机的localhost),修改后:
private apiUrl = 'http://10.0.2.2:3000/notify';
3. 验证网络连通性
- 在Android设备的浏览器中访问
http://你的局域网IP:3000,确认能正常连接(即使返回404也没关系,只要不是无法访问) - 确保电脑防火墙允许3000端口的入站连接
4. 额外检查
- 确认Android设备已成功获取FCM令牌(
this.token不为空) - 后端的CORS配置已启用(你的代码中已使用
cors()中间件,无需额外修改)
内容的提问来源于stack exchange,提问作者senanurr
相关产品推荐
相关产品推荐

