简体   繁体   English

Firebase 云消息传递 - Django - 我如何服务工作者?

[英]Firebase Cloud Messaging - Django - how do I service worker?

Update from a place of mild understanding:从温和理解的地方更新:

I have read much about Service Workers and advanced the project I'm working on far past where it was when I wrote this.我已经阅读了很多关于 Service Workers 的文章,并将我正在从事的项目推进到了我写这篇文章的时候。

The main issue I was exploring here (getting firebase-messaging-sw.js served from the root of the domain) was solved by the ServiceWorkerView below.我在这里探索的主要问题(从域的根目录获取firebase-messaging-sw.js服务)由下面的ServiceWorkerView解决。

Fun fact: turns out that the reason to have firebase-messaging-sw.js served from the root of the domain is that the scope of a Service Worker's control is defined by the URL.有趣的事实:事实证明,从域的根目录提供firebase-messaging-sw.js的原因是 Service Worker 的控制范围由 URL 定义。 So, while:所以,虽然:

http://whatever.com/firebase-messaging-sw.js has control over everything, http://whatever.com/firebase-messaging-sw.js可以控制一切,

http://whatever.com/static/firebase-messaging-sw.js only has control over everything under /static/ (in this case, only the static resources served by Django). http://whatever.com/static/firebase-messaging-sw.js只能控制/static/下的所有内容(在这种情况下,只有 Django 提供的静态资源)。 Even though the JS file is static, it needs to control pages outside that path.即使 JS 文件是静态的,它也需要控制该路径之外的页面。

A good starting point for learning about Service Workers 学习 Service Worker 的好起点

Original Question:原问题:

How do I get the FCM service worker ( firebase-messaging-sw.js ) to work in Django?如何让 FCM 服务工作者 ( firebase-messaging-sw.js ) 在 Django 中工作?

I'm using django with fcm-django .我正在将djangofcm-django

How do I serve firebase-messaging-sw.js at the root?如何在根目录firebase-messaging-sw.js

Django serves static assets by default under the path “/static/“. Django 默认在“/static/”路径下提供静态资源。 Changing the STATIC_URL to “/“ makes firebase-messaging-sw.js available but nothing else is..将 STATIC_URL 更改为“/”使firebase-messaging-sw.js可用,但没有其他内容......

I tried a suggestion to put the following in my urls.py:我尝试了将以下内容放入我的 urls.py 的建议:

url(r'^(?!/static/.*)(?P<path>.*\\..*)$', RedirectView.as_view(url='/static/%(path)s'))

While clearly a glorious use of regex magic, it turns out that the service worker can't be behind a redirect.虽然显然是对正则表达式魔法的光荣使用,但事实证明服务工作者不能支持重定向。 (I guess this also has to do with the fun fact) (我想这也与有趣的事实有关)

I wrote a view pointed to by an explicit URL in the urlconf.我在 urlconf 中写了一个由显式 URL 指向的视图。 that works.那个有效。

class ServiceWorkerView(View):
    def get(self, request, *args, **kwargs):
        return render(request, 'fcmtest/firebase-messaging-sw.js')

urls.py:网址.py:

path('firebase-messaging-sw.js', views.ServiceWorkerView.as_view(), name='service_worker')

Now the url http://localhost:8000/firebase-messaging-sw.js serves up the javascript;现在 url http://localhost:8000/firebase-messaging-sw.js提供了 javascript; however, Firebase complains that the content-type is text/plain然而,Firebase 抱怨内容类型是text/plain

So I changed the view to:所以我将视图更改为:

class ServiceWorkerView(View):
    def get(self, request, *args, **kwargs):
        return render(request, 'fcmtest/firebase-messaging-sw.js', content_type="application/x-javascript")

And the error I get is:我得到的错误是:

Uncaught (in promise) TypeError: Failed to register a ServiceWorker: A bad HTTP response code (404) was received when fetching the script. Uncaught (in promise) TypeError: Failed to register a ServiceWorker: 获取脚本时收到错误的 HTTP 响应代码 (404)。

My firebase-messaging-sw.js (note none of the console.log statements print):firebase-messaging-sw.js (注意没有打印 console.log 语句):

importScripts("https://www.gstatic.com/firebasejs/5.2.0/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/5.2.0/firebase-messaging.js");
importScripts("https://www.gstatic.com/firebasejs/5.2.0/init.js");

// Initialize Firebase
var config = {
    apiKey: "AIzaSyBOb5gh1Lry84116M1uvAS_xnKtcWUoNlA",
    authDomain: "pyfcm-5f794.firebaseapp.com",
    databaseURL: "https://pyfcm-5f794.firebaseio.com",
    projectId: "pyfcm-5f794",
    storageBucket: "pyfcm-5f794.appspot.com",
    messagingSenderId: "813943398064"
};
console.log('in service worker - before initializeApp')
firebase.initializeApp(config);
console.log('in service worker - after initializeApp')

const messaging = firebase.messaging();
console.log('in service worker - after firebase.messaging')

// if the app is in the background (user not on page)
messaging.setBackgroundMessageHandler(function(payload) {
    console.log('in service worker - in setBackgroundMessageHandler')
    const title = "Hello World";
    const options = {
        body: payload.data.status
    }
    return self.registration.showNotification(title, options);
});

and finally, my device registration page (I'm not sure how to run the javascript and then submit. form.submit() didn't work but that might be because of other things not working..? ( fcm/fcmtest/templates/fcmtest/device_registration.html ):最后,我的设备注册页面(我不确定如何运行 javascript 然后提交。form.submit() 不起作用,但这可能是因为其他事情不起作用..?( fcm/fcmtest/templates/fcmtest/device_registration.html ):

<head>
    {% load static %}
    <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>FCM Device Registration</title>

    <!-- update the version number as needed -->
    <script src="https://www.gstatic.com/firebasejs/5.2.0/firebase.js"></script>
    <script>
    // Initialize Firebase
    var config = {
    apiKey: "AIzaSyBOb5gh1Lry84116M1uvAS_xnKtcWUoNlA",
    authDomain: "pyfcm-5f794.firebaseapp.com",
    databaseURL: "https://pyfcm-5f794.firebaseio.com",
    projectId: "pyfcm-5f794",
    storageBucket: "pyfcm-5f794.appspot.com",
    messagingSenderId: "813943398064"
    };

    firebase.initializeApp(config);
    </script>

    <script defer src="https://www.gstatic.com/firebasejs/5.2.0/firebase-app.js"></script>
    <!-- include only the Firebase features as you need -->
    <script defer src="https://www.gstatic.com/firebasejs/5.2.0/firebase-auth.js"></script>
    <!--<script defer src="https://www.gstatic.com/firebasejs/5.2.0/firebase-database.js"></script>-->
    <script defer src="https://www.gstatic.com/firebasejs/5.2.0/firebase-messaging.js"></script>
    <!--<script defer src="https://www.gstatic.com/firebasejs/5.2.0/firebase-storage.js"></script>-->
    <!-- initialize the SDK after all desired features are loaded -->
    <!--<script defer src="https://www.gstatic.com/firebasejs/5.2.0/init.js"></script>-->

</head>

<body>

<script>

function deviceRegistration() {
    console.log('in deviceRegistration()')
    firebase.initializeApp(config);
    const messaging = firebase.messaging();

    // public key generated in firebase console
    messaging.usePublicVapidKey("BMEoHrnzLq5WNeyahbSxJNTyS-44bXug2wetxAWVMLMSUIQE0dexhP4pJhcSA-ZlQlneHURmYQcnq9ofym_sStY");
    // console.log('{% static "firebase-messaging-sw.js" %}')
    console.log("/firebase-messaging-sw.js")
    // changed location passed to navigator.serviceWorker.register since I am no longer serving firebase-messaging.js from localhost/static/ (see that url hack)
    // navigator.serviceWorker.register('{% static "firebase-messaging-sw.js" %}')
    navigator.serviceWorker.register("/firebase-messaging-sw.js")
    .then((registration) => {
        console.log('before messaging.useServiceWorker')
        messaging.useServiceWorker(registration);
        console.log('after messaging.useServiceWorker')

        // Request permission and get token.....
        messaging.requestPermission()
        .then(function(){
            console.log("Have Permission");
        })
        .then(function(token) {
            form = document.getElementById("registration_form");

            registration_token = messaging.getToken();
            form.registration_id = registration_token;

            var isAndroid = navigator.userAgent.toLowerCase().indexOf("android") >= -1;
            if (isAndroid) {
                form.type = "android";
            } else {
                var isiOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
                if (isiOS) {
                    form.type = "ios";
                } else {
                    form.type = "web";
                }
            }

            //form.submit();
        })
        .catch(function(err){
            console.log("Permission denied");
        })
    });

    // request permission to send notifications
    messaging.requestPermission()
        .then(function(){
            console.log("Have Permission");
        })
        .then(function(token) {
            form = document.getElementById("registration_form");

            registration_token = messaging.getToken();
            form.registration_id = registration_token;

            var isAndroid = navigator.userAgent.toLowerCase().indexOf("android") >= -1;
            if (isAndroid) {
                form.type = "android";
            } else {
                var isiOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
                if (isiOS) {
                    form.type = "ios";
                } else {
                    form.type = "web";
                }
            }

            //form.submit();
        })
        .catch(function(err){
            console.log("Permission denied");
        })

    // if user is on the page then show message directly instead of a notification
    messaging.onMessage(function(payload) {
        console.log('payload: ', payload);
    });

}

</script>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}


<form id="registration_form" method="post">{% csrf_token %}
    <input id="name" type="text" />
    <input id="type" type="hidden" />
    <input id="user" type="hidden" value="{{ user.id }}" />
    <input id="registration_id" type="hidden" />
    <input id="btn_register" type="button" value="Register" onclick="deviceRegistration();" />
</form>

</body>

I tried adding a manifest.json as per instructions on https://firebase.google.com/docs/cloud-messaging/js/client我尝试按照https://firebase.google.com/docs/cloud-messaging/js/client 上的说明添加manifest.json

It is currently served from my static files and is linked in the registration and send pages它目前由我的静态文件提供,并在注册和发送页面中链接

The errors in my console:我的控制台中的错误:

Uncaught (in promise) TypeError: Failed to register a ServiceWorker: A bad HTTP response code (404) was received when fetching the script. Uncaught (in promise) TypeError: Failed to register a ServiceWorker: 获取脚本时收到错误的 HTTP 响应代码 (404)。

A bad HTTP response code (404) was received when fetching the script.获取脚本时收到错误的 HTTP 响应代码 (404)。

Failed to load resource: net::ERR_INVALID_RESPONSE无法加载资源:net::ERR_INVALID_RESPONSE

firebase-messaging-sw.js:3 Uncaught firebase-messaging-sw.js:3 未捕获

(anonymous) @ firebase-messaging-sw.js:3 (匿名)@ firebase-messaging-sw.js:3

index.esm.js:1945 Uncaught (in promise) index.esm.js:1945 未捕获(承诺)

Object目的

browserErrorMessage:浏览器错误消息:

"Failed to register a ServiceWorker: ServiceWorker script evaluation failed" “注册ServiceWorker失败:ServiceWorker脚本评估失败”

code:代码:

"messaging/failed-serviceworker-registration" “消息/失败的服务人员注册”

message:信息:

"Messaging: We are unable to register the default service worker. Failed to register a ServiceWorker: ServiceWorker script evaluation failed (messaging/failed-serviceworker-registration)." “消息:我们无法注册默认的服务工作者。注册服务工作者失败:服务工作者脚本评估失败(消息/失败-serviceworker-registration)。”

stack:堆:

"FirebaseError: Messaging: We are unable to register the default service worker. Failed to register a ServiceWorker: ServiceWorker script evaluation failed (messaging/failed-serviceworker-registration).↵ at https://www.gstatic.com/firebasejs/5.2.0/firebase-messaging.js:1:34104 " “FirebaseError: Messaging: 我们无法注册默认的 Service Worker。无法注册 ServiceWorker:ServiceWorker 脚本评估失败 (messaging/failed-serviceworker-registration)。↵ at https://www.gstatic.com/firebasejs/5.2 .0/firebase-messaging.js:1:34104 "

The issue of not being able to serve firebase-messaging-sw.js can be solved by creating a ServiceWorkerView to GET the file directly and hardcoding the URLconf .无法为firebase-messaging-sw.js提供服务的问题可以通过创建ServiceWorkerView来直接GET文件并硬编码URLconf

fcm/fcmtest/views.py: fcm/fcmtest/views.py:

class ServiceWorkerView(View):
    def get(self, request, *args, **kwargs):
        return render(request, 'fcmtest/firebase-messaging-sw.js', content_type="application/x-javascript")

fcm/fcmtest/urls.py fcm/fcmtest/urls.py

app_name = 'fcmtest'
urlpatterns = [
    ...
    path('firebase-messaging-sw.js', views.ServiceWorkerView.as_view(), name='service_worker')
]

The reason to serve firebase-messaging-sw.js from the root of the domain is that the scope of a Service Worker's control is defined by the URL path.从域的根目录提供firebase-messaging-sw.js的原因是 Service Worker 的控制范围由 URL 路径定义。 Everything under the path the Service Worker is available at is under its control. Service Worker 可用的路径下的所有内容都在其控制之下。


The 404 might be due to a javascript error in device_registration.html . 404可能是由于device_registration.html的 javascript 错误device_registration.html config is referenced but has not been assigned to. config被引用但尚未分配给。

function deviceRegistration() {
    firebase.initializeApp(config);

Also, it is not necessary (maybe even problematic) to do this with Firebase Cloud Messaging:此外,没有必要(甚至可能有问题)使用 Firebase Cloud Messaging 执行此操作:

navigator.serviceWorker.register("/firebase-messaging-sw.js")
.then((registration) => {
    messaging.useServiceWorker(registration);
})

Instead, use firebase.initializeApp(config);相反,使用firebase.initializeApp(config);


It might be a good idea to serve manifest.json at the root.在根目录提供manifest.json可能是个好主意。


The issue with form.submit() is that the code to alter the elements in the form from the promise is incomplete. form.submit()的问题在于,从 promise 更改表单中元素的代码不完整。 After debugging that it will work great.调试后它会很好用。

I like what Inversus has done because it will work in dev/test/prod.我喜欢 Inversus 所做的,因为它可以在 dev/test/prod 中工作。 That being said, in production I have elected to let nginx serve the firebase-messaging-sw.js file .话虽如此,在生产中我选择让 nginx 服务 firebase-messaging-sw.js 文件。 . . . .

    # Firebase Cloud Messaging (FCM) requires this file to be served from
    # your root directory
    location =/firebase-messaging-sw.js {
        root /home/ubuntu/yourpath/yourproject/FCM/;
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我可以完全在 web 服务工作者中运行 Firebase 云消息传递吗? - Can I run Firebase Cloud Messaging entirely in a web service worker? Chrome中的Firebase Cloud Messaging Service Worker重定向 - Firebase Cloud Messaging Service Worker redirection in Chrome Firebase Cloud Messaging Service Worker和self.addEventListener的问题 - Issue with Firebase Cloud Messaging Service Worker and self.addEventListener Chrome上的Firebase Messaging服务人员 - Firebase Messaging service worker on chrome Firebase 消息传递和 Firebase 托管中的服务工作者错误 - Firebase Service worker error in Messaging and Firebase Hosting 如何正确注销firebase消息传递所安装的服务工作者 - How to properly unregister service worker installed by firebase messaging Service Worker 仅显示第一个推送通知(来自云消息传递),直到我重新加载 - 工作人员收到消息 - Service Worker only showing first push notification (from cloud messaging) until I reload - message IS received by the worker 如何为我的 Firebase Cloud Messaging 通知提供标签属性? - How do I give my Firebase Cloud Messaging notification a tag property? firebase 云消息传递给 A bad HTTP 响应代码 (404) 单击“允许”并手动添加服务工作者不起作用 - firebase cloud messaging gives A bad HTTP response code (404) after clicking on 'allow' and adding service worker manually doesn't work 如何从 javascript 发送 firebase 云消息通知? - How can I send a firebase cloud messaging notification from javascript?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM