Django+React集成Firebase推送通知:ServiceWorker注册失败解决
问题描述
我开发的Web应用采用Django Rest Framework后端+React前端架构,集成Firebase推送通知服务时遇到生产环境异常:
开发环境下(React运行于localhost:3000,Django运行于127.0.0.1:8000)能正常生成FCM令牌并接收通知,但生产环境中用npm run build构建React后由Django托管时,出现以下错误:
FirebaseError: Messaging: We are unable to register the default service worker. Failed to register a ServiceWorker for scope ('https://mywebsite.in/firebase-cloud-messaging-push-scope') with script ('https://mywebsite.in/firebase-messaging-sw.js'): The script has an unsupported MIME type ('text/html'). (messaging/failed-service-worker-registration). at registerDefaultSw.ts:43:25 at p (regeneratorRuntime.js:44:17) at Generator.<anonymous> (regeneratorRuntime.js:125:22) at Generator.throw (regeneratorRuntime.js:69:21) at Td (asyncToGenerator.js:3:20) at s (asyncToGenerator.js:25:9)
我的Firebase相关配置文件如下:
firebase-messaging-sw.js(React public目录)
importScripts( "https://www.gstatic.com/firebasejs/9.0.0/firebase-app-compat.js" ); importScripts( "https://www.gstatic.com/firebasejs/9.0.0/firebase-messaging-compat.js" ); const firebaseConfig = { ... }; firebase.initializeApp(firebaseConfig); const messaging = firebase.messaging(); messaging.onBackgroundMessage((payload) => { const notificationTitle = payload.notification.title; const notificationOptions = { body: payload.notification.body, icon: payload.notification.icon, }; console.log(notificationOptions, notificationTitle); self.registration.showNotification(notificationTitle, notificationOptions); }); }); // 注意:此处存在多余的闭合括号,会导致脚本语法错误
firebase.js(React src目录)
import { initializeApp } from "firebase/app"; import { getMessaging } from "firebase/messaging"; const firebaseConfig = { ... }; export const app = initializeApp(firebaseConfig); export const messaging = getMessaging(app);
FCM令牌生成代码
const requestPermission = async () => { if (Notification.permission === "default") { if (user.regd_as === "TCH") { NotificationBar( `Please click allow for notification accesss so that you can receive notifications when students send you a request.`, `info` ); } else { NotificationBar( `Please click allow for notification accesss so that you can receive notifications when teacher accept your request.`, `info` ); } } try { const permission = await Notification.requestPermission(); if (permission === "granted") { // Generate token const token = await getToken(messaging, { vapidKey: "BJueTNhdRYtjQJ3BqwytPu4MUkK0BrUkuPcnDd3oBCoppBTb-xdJtaRcmu9CLStX6UeltYst--YtMirduz3vGJI", }); if (token) { console.log(token); let response = await storeNotificationToken(token); } else { console.log(`token was not produced`); } } else if (permission === "denied") { if (localStorage.getItem("noti_denied_seen") === "true") { return; } if (user.regd_as === "TCH") { NotificationBar( `Since you have denied notification permission you will miss out notifications when students send you request. Please enable/ allow notification access.`, `error` ); } else { NotificationBar( `Since you have denied notification permission you will miss out notifications when teachers aceepts your request. Please enable/ allow notification access.`, `error` ); } localStorage.setItem("noti_denied_seen", "true"); } } catch (error) { console.log(error); } }; // 获取用户通知权限 useEffect(() => { requestPermission(); }, []);
解决方案
错误核心原因是Django返回firebase-messaging-sw.js时使用了text/html MIME类型,而ServiceWorker要求必须以application/javascript类型返回。同时需修正脚本语法错误、确保文件路径可访问。
步骤1:修复ServiceWorker脚本语法错误
删除firebase-messaging-sw.js末尾多余的});,修正后文件结尾应为:
messaging.onBackgroundMessage((payload) => { // 原有逻辑代码 }); // 移除最后多余的闭合括号
步骤2:确保ServiceWorker文件在Django静态资源目录中
- 执行
npm run build后,React会将public目录下的文件复制到build目录根目录 - 将
build目录内的所有文件复制到Django项目的静态资源目录(如myproject/static/react_build/),或直接配置Django将React的build目录作为静态资源根路径
步骤3:配置Django返回正确的MIME类型
方法A:单独映射ServiceWorker路由
在项目根urls.py中添加:
from django.http import FileResponse from django.conf import settings import os def serve_firebase_sw(request): # 根据实际静态资源路径调整 sw_path = os.path.join(settings.STATIC_ROOT, 'firebase-messaging-sw.js') return FileResponse(open(sw_path, 'rb'), content_type='application/javascript') urlpatterns = [ # 其他已有路由 path('firebase-messaging-sw.js', serve_firebase_sw), ]
方法B:全局配置JS文件MIME类型
在settings.py中添加:
import mimetypes mimetypes.add_type("application/javascript", ".js", True)
此方法会确保所有.js文件都以正确的MIME类型返回,无需单独配置单个文件。
步骤4:显式注册ServiceWorker(可选但更可靠)
默认Firebase会自动注册ServiceWorker,但在Django托管场景下,显式注册可避免路径问题。修改firebase.js:
import { initializeApp } from "firebase/app"; import { getMessaging } from "firebase/messaging"; const firebaseConfig = { ... }; export const app = initializeApp(firebaseConfig); export const messaging = getMessaging(app); // 显式注册ServiceWorker export const registerSw = async () => { if ('serviceWorker' in navigator) { try { const registration = await navigator.serviceWorker.register('/firebase-messaging-sw.js'); console.log('Service Worker 注册成功:', registration); await messaging.useServiceWorker(registration); } catch (error) { console.error('Service Worker 注册失败:', error); } } };
然后在获取权限前先执行注册:
useEffect(() => { registerSw().then(() => { requestPermission(); }); }, []);
步骤5:验证配置
- 部署修改后的代码到生产环境
- 访问
https://yourdomain.in/firebase-messaging-sw.js,检查响应头的Content-Type是否为application/javascript - 打开浏览器控制台,确认ServiceWorker注册成功且无错误提示
内容的提问来源于stack exchange,提问作者Ritankar Bhattacharjee
相关产品推荐
相关产品推荐

