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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 14:20:53