[英]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
.我正在将
django
与fcm-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.