jira推送api
The Push API allows a web app to receive messages pushed by a server, even if the web app is not currently open in the browser or not running on the device
即使Web应用程序当前未在浏览器中打开或未在设备上运行,Push API也允许Web应用程序接收服务器推送的消息。
Using the Push API you can send messages to your users, pushing them from the server to the client, even when the user is not browsing the site.
使用Push API,即使用户没有浏览站点,您也可以向用户发送消息,将消息从服务器推送到客户端。
This lets you deliver notifications and content updates, giving you the ability to have a more engaged audience.
这使您可以发送通知和内容更新,使您能够吸引更多的受众。
This is huge because one of the missing pillars of the mobile web, compared to native apps, was the ability to receive notifications, along with offline support.
这是巨大的,因为与本机应用程序相比,移动网络缺少的Struts之一是能够接收通知以及脱机支持。
有很好的支持吗? (Is it well supported?)
The Push API is a recent addition to the browser APIs, and it’s currently supported by Chrome (Desktop and Mobile), Firefox and Opera since 2016, Edge since version 17 (early 2018). See more about the current state of browsers support at https://caniuse.com/#feat=push-api
Push API是浏览器API的最新功能,Chrome(台式机和移动设备),Firefox和Opera自2016年以来一直受Edge支持,而Edge自版本17(2018年初)以来受到支持。 在https://caniuse.com/#feat=push-api上了解有关浏览器当前支持状态的更多信息
IE does not support it, and Safari has its own implementation.
IE不支持它,而Safari有其自己的实现 。
Since Chrome and Firefox support it, approximately 60% of the users browsing on the desktop have access to it, so it’s quite safe to use.
由于Chrome和Firefox支持它,因此大约60%的在桌面上浏览的用户都可以访问它,因此使用起来非常安全 。
这个怎么运作 (How it works)
总览 (Overview)
When a user visits your web app, you can trigger a panel asking permission to send updates. A Service Worker is installed, and operating in the background listens for a Push Event.
当用户访问您的Web应用程序时,您可以触发一个面板,询问是否允许发送更新。 已安装Service Worker ,并且在后台运行以侦听Push Event 。
Push and Notifications are a separate concept and API, sometimes mixed because of the push notifications term used in iOS. Basically, the Notifications API is invoked when a push event is received using the Push API.
推送和通知是一个单独的概念和API,有时会混为一谈,因为iOS中使用了推送通知术语。 基本上,当使用Push API接收到push事件时,将调用Notifications API。
Your server sends the notification to the client, and the Service Worker, if given permission, receives a push event. The Service Worker reacts to this event by triggering a notification.
您的服务器将通知发送到客户端,并且如果获得许可,则Service Worker会收到push事件 。 服务人员通过触发通知来对此事件做出React。
获得用户的许可 (Getting the user’s permission)
The first step in working with the Push API is getting the user’s permission to receive data from you.
使用Push API的第一步是获得用户的许可,以从您接收数据。
Many sites implement this panel badly, showing it on the first page load. The user is not yet convinced your content is good, and they will deny the permission. Do it wisely.
许多站点在实施此面板时效果很差,在首页加载时就显示出来。 用户尚未确信您的内容很好,他们将拒绝该许可。 明智地做。
There are 6 steps:
共有6个步骤:
- Check if Service Workers are supported 检查服务工作者是否受支持
- Check if the Push API is supported 检查是否支持Push API
- Register a Service Worker 注册服务人员
- Request permission from the user 向用户请求权限
- Subscribe the user and get the PushSubscription object 订阅用户并获取PushSubscription对象
- Send the PushSubscription object to your server 将PushSubscription对象发送到您的服务器
检查服务工作者是否受支持 (Check if Service Workers are supported)
if (!('serviceWorker' in navigator)) {
// Service Workers are not supported. Return
return
}
检查是否支持Push API (Check if the Push API is supported)
if (!('PushManager' in window)) {
// The Push API is not supported. Return
return
}
注册服务人员 (Register a Service Worker)
This code register the Service Worker located in the worker.js
file placed in the domain root:
此代码注册位于域根目录下的worker.js
文件中的Service Worker:
window.addEventListener('load', () => {
navigator.serviceWorker.register('/worker.js')
.then((registration) => {
console.log('Service Worker registration completed with scope: ',
registration.scope)
}, (err) => {
console.log('Service Worker registration failed', err)
})
})
To know more about how Service Workers work in detail, check out the Service Workers guide.
要了解有关服务人员详细工作方式的更多信息,请查阅《 服务人员指南》 。
向用户请求权限 (Request permission from the user)
Now that the Service worker is registered, you can request the permission.
现在,服务工作者已注册,您可以请求权限。
The API to do this changed over time, and it went from accepting a callback function as a parameter to returning a Promise, breaking the backward and forward compatibility, and we need to do both as we don’t know which approach is implemented by the user’s browser.
执行此操作的API随时间而变化,从接受回调函数作为参数到返回Promise ,破坏了向后和向前的兼容性,我们都需要做这两项,因为我们不知道该方法实现了哪种方法。用户的浏览器。
The code is the following, calling Notification.requestPermission()
.
代码如下,调用Notification.requestPermission()
。
const askPermission = () => {
return new Promise((resolve, reject) => {
const permissionResult = Notification.requestPermission((result) => {
resolve(result)
})
if (permissionResult) {
permissionResult.then(resolve, reject)
}
})
.then((permissionResult) => {
if (permissionResult !== 'granted') {
throw new Error('Permission denied')
}
})
}
The permissionResult
value is a string, that can have the value of: - granted
- default
- denied
permissionResult
值是一个字符串,其值可以是:- granted
- default
- denied
This code causes the browser to show the permission dialogue:
此代码使浏览器显示权限对话框:
![The browser permission dialogue](https://i-blog.csdnimg.cn/blog_migrate/4e5c1634cb92cc4d832c980b720aff77.png)
If the user clicks Block, you won’t be able to ask for the user’s permission any more, unless they manually go and unblock the site in an advanced settings panel in the browser (very unlikely to happen).
如果用户单击“阻止”,则您将无法再请求该用户的许可 ,除非他们在浏览器的高级设置面板中手动进行并取消阻止该站点(极不可能发生)。
订阅用户并获取PushSubscription对象 (Subscribe the user and get the PushSubscription object)
If the user gave us permission, we can subscribe it and by calling registration.pushManager.subscribe()
.
如果用户授予我们许可,我们可以通过调用registration.pushManager.subscribe()
进行订阅。
const APP_SERVER_KEY = 'XXX'
window.addEventListener('load', () => {
navigator.serviceWorker.register('/worker.js')
.then((registration) => {
askPermission().then(() => {
const options = {
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(APP_SERVER_KEY)
}
return registration.pushManager.subscribe(options)
}).then((pushSubscription) => {
// we got the pushSubscription object
}
}, (err) => {
console.log('Service Worker registration failed', err)
})
})
APP_SERVER_KEY
is a string - called Application Server Key or VAPID key - that identifies the application public key, part of a public / private key pair.
APP_SERVER_KEY
是一个字符串-称为应用程序服务器密钥或VAPID密钥 -标识应用程序公共密钥,它是公共/私有密钥对的一部分。
It will be used as part of the validation that for security reasons occurs to make sure you (and only you, not someone else) can send a push message back to the user.
出于安全原因,它将用作验证的一部分,以确保您(只有您自己,而不是其他人)可以将推送消息发送回用户。
将PushSubscription对象发送到您的服务器 (Send the PushSubscription object to your server)
In the previous snippet we got the pushSubscription
object, which contains all we need to send a push message to the user. We need to send this information to our server, so we’re able to send notifications later on.
在上一个代码段中,我们获得了pushSubscription
对象,该对象包含将推送消息发送给用户所需的全部内容。 我们需要将此信息发送到我们的服务器,以便稍后能够发送通知。
We first create a JSON representation of the object
我们首先创建对象的JSON表示形式
const subscription = JSON.stringify(pushSubscription)
and we can post it to our server using the Fetch API:
我们可以使用Fetch API将其发布到我们的服务器中:
const sendToServer = (subscription) => {
return fetch('/api/subscription', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(subscription)
})
.then((res) => {
if (!res.ok) {
throw new Error('An error occurred')
}
return res.json()
})
.then((resData) => {
if (!(resData.data && resData.data.success)) {
throw new Error('An error occurred')
}
})
}
sendToServer(subscription)
Server-side, the /api/subscription
endpoint receives the POST request and can store the subscription information into its storage.
在服务器端, /api/subscription
端点接收POST请求,并将订阅信息存储到其存储中。
服务器端如何工作 (How the Server side works)
So far we only talked about the client-side part: getting a user’s permission to be notified in the future.
到目前为止,我们仅讨论了客户端部分:在将来获得用户通知的权限。
What about the server? What should it do, and how should it interact with the client?
那服务器呢? 它应该做什么,以及如何与客户互动?
These server-side examples uses Express.js (http://expressjs.com/) as the base HTTP framework, but you can write a server-side Push API handler in any language or framework
这些服务器端示例使用Express.js( http://expressjs.com/ )作为基本HTTP框架,但是您可以使用任何语言或框架编写服务器端Push API处理程序
注册新的客户订阅 (Registering a new client subscription)
When the client sends a new subscription, remember we used the /api/subscription
HTTP POST endpoint, sending the PushSubscription object details in JSON format, in the body.
当客户端发送新的订阅时,请记住我们使用了/api/subscription
HTTP POST端点,在正文中以JSON格式发送了PushSubscription对象的详细信息。
We initialize Express.js:
我们初始化Express.js:
const express = require('express')
const app = express()
This utility function makes sure the request is valid, has a body and an endpoint property, otherwise it returns an error to the client:
此实用程序功能可确保请求有效,具有正文和终结点属性,否则它将向客户端返回错误:
const isValidSaveRequest = (req, res) => {
if (!req.body || !req.body.endpoint) {
res.status(400)
res.setHeader('Content-Type', 'application/json')
res.send(JSON.stringify({
error: {
id: 'no-endpoint',
message: 'Subscription must have an endpoint'
}
}))
return false
}
return true
}
The next utility function saves the subscription to the database, returning a promise resolved when the insertion completed (or failed). The insertToDatabase
function is a placeholder, we’re not going into those details here:
下一个实用程序功能将预订保存到数据库,并在插入完成(或失败)时返回已解决的promise。 insertToDatabase
函数是一个占位符,在此不再赘述:
const saveSubscriptionToDatabase = (subscription) => {
return new Promise((resolve, reject) => {
insertToDatabase(subscription, (err, id) => {
if (err) {
reject(err)
return
}
resolve(id)
})
})
}
We use those functions in the POST request handler below. We check if the request is valid, then we save the request and then we return a data.success: true
response back to the client, or an error:
我们在下面的POST请求处理程序中使用这些功能。 我们检查请求是否有效,然后保存请求,然后返回data.success: true
返回给客户端的data.success: true
响应或错误:
app.post('/api/subscription', (req, res) => {
if (!isValidSaveRequest(req, res)) {
return
}
saveSubscriptionToDatabase(req, res.body)
.then((subscriptionId) => {
res.setHeader('Content-Type', 'application/json')
res.send(JSON.stringify({ data: { success: true } }))
})
.catch((err) => {
res.status(500)
res.setHeader('Content-Type', 'application/json')
res.send(JSON.stringify({
error: {
id: 'unable-to-save-subscription',
message: 'Subscription received but failed to save it'
}
}))
})
})
app.listen(3000, () => {
console.log('App listening on port 3000')
})
发送推送消息 (Sending a Push message)
Now that the server has registered the client in its list, we can send it Push messages. Let’s see how that works by creating an example code snippet that fetches all subscriptions and sends a Push message to all of them at the same time.
现在,服务器已在其列表中注册了客户端,我们可以向其发送Push消息。 让我们通过创建一个示例代码片段来查看其工作原理,该代码片段将提取所有订阅并同时向所有订阅发送一个Push消息。
We use a library because the Web Push protocol is complex, and a lib allows us to abstract away a lot of low level code that makes sure we can work safely and correctly handle any edge case.
我们使用库是因为Web Push协议很复杂 ,而lib使我们可以抽象出许多低级代码,以确保我们可以安全地工作并正确处理任何边缘情况。
This example uses the
web-push
Node.js library (https://github.com/web-push-libs/web-push) to handle sending the Push message本示例使用
web-push
Node.js库( https://github.com/web-push-libs/web-push )处理发送Push消息
We first initialize the web-push
lib, and we generate a tuple of private and public keys, and set them as the VAPID details:
我们首先初始化web-push
lib,然后生成一个私有和公共密钥的元组,并将它们设置为VAPID详细信息:
const webpush = require('web-push')
const vapidKeys = webpush.generateVAPIDKeys()
const PUBLIC_KEY = 'XXX'
const PRIVATE_KEY = 'YYY'
const vapidKeys = {
publicKey: PUBLIC_KEY,
privateKey: PRIVATE_KEY
}
webpush.setVapidDetails(
'mailto:my@email.com',
vapidKeys.publicKey,
vapidKeys.privateKey
)
Then we set up a triggerPush()
method, responsible for sending the push event to a client. It just calls webpush.sendNotification()
and catches any error. If the return error HTTP status code is 410, which means gone, we delete that subscriber from the database.
然后,我们设置一个triggerPush()
方法,该方法负责将push事件发送到客户端。 它只是调用webpush.sendNotification()
并捕获任何错误。 如果返回错误的HTTP状态代码为410 (表示走了) ,我们将从数据库中删除该订户。
const triggerPush = (subscription, dataToSend) => {
return webpush.sendNotification(subscription, dataToSend)
.catch((err) => {
if (err.statusCode === 410) {
return deleteSubscriptionFromDatabase(subscription._id)
} else {
console.log('Subscription is no longer valid: ', err)
}
})
}
We don’t implement getting the subscriptions from the database, but we leave it as a stub:
我们没有实现从数据库获取订阅,但是将其保留为存根:
const getSubscriptionsFromDatabase = () => {
//stub
}
The meat of the code is the callback of the POST request to the /api/push
endpoint:
代码的实质是POST请求对/api/push
端点的回调:
app.post('/api/push', (req, res) => {
return getSubscriptionsFromDatabase()
.then((subscriptions) => {
let promiseChain = Promise.resolve()
for (let i = 0; i < subscriptions.length; i++) {
const subscription = subscriptions[i]
promiseChain = promiseChain.then(() => {
return triggerPush(subscription, dataToSend)
})
}
return promiseChain
})
.then(() => {
res.setHeader('Content-Type', 'application/json')
res.send(JSON.stringify({ data: { success: true } }))
})
.catch((err) => {
res.status(500)
res.setHeader('Content-Type', 'application/json')
res.send(JSON.stringify({
error: {
id: 'unable-to-send-messages',
message: `Failed to send the push ${err.message}`
}
}))
})
})
What the above code does is: it gets all the subscriptions from the database, then it iterates on them, and it calls the triggerPush()
function we explained before.
上面的代码所做的是:它从数据库中获取所有订阅,然后对它们进行迭代,并调用我们之前介绍的triggerPush()
函数。
Once the subscriptions are done, we return a successful JSON response, unless an error occurred and we return a 500 error.
订阅完成后,我们将返回成功的JSON响应,除非发生错误并且返回500错误。
在现实世界… (In the real world…)
It’s unlikely that you’ll set up your own Push server unless you have a very special use case, or you just want to learn the tech or you like to DIY. Instead, you usually want to use platforms such as OneSignal (https://onesignal.com) which transparently handle Push events to all kind of platforms, Safari and iOS included, for free.
除非您有一个非常特殊的用例,或者您只是想学习技术或喜欢DIY,否则不太可能设置自己的Push服务器。 取而代之的是,您通常希望使用OneSignal( https://onesignal.com )之类的平台,该平台可以透明地免费处理所有平台(包括Safari和iOS)的Push事件。
接收推送事件 (Receive a Push event)
When a Push event is sent from the server, how does the client get it?
从服务器发送Push事件时,客户端如何获取它?
It’s a normal JavaScript event listener, on the push
event, which runs inside a Service Worker:
这是普通的JavaScript事件侦听器,用于push
事件,该事件在Service Worker内部运行:
self.addEventListener('push', (event) => {
// data is available in event.data
})
event.data
contains the PushMessageData
object which exposes methods to retrieve the push data sent by the server, in the format you want:
event.data
包含PushMessageData
对象,该对象以所需的格式公开用于检索服务器发送的推送数据的方法:
arrayBuffer() : as an
ArrayBuffer
objectarrayBuffer() :作为
ArrayBuffer
对象blob(): as a Blob object
blob() :作为Blob对象
json(): parsed as JSON
json() :解析为JSON
text(): plain text
text() :纯文本
You’ll normally use event.data.json()
.
通常,您将使用event.data.json()
。
显示通知 (Displaying a notification)
Here we intersect a bit with the Notifications API, but for a good reason, as one of the main use cases of the Push API is to display notifications.
在这里,我们与Notifications API进行了一些交叉,但这是有充分的理由的,因为Push API的主要用例之一是显示通知。
Inside our push
event listener in the Service Worker, we need to display the notification to the user, and to tell the event to wait until the browser has shown it before the function can terminate. We extend the event lifetime until the browser has done displaying the notification (until the promise has been resolved), otherwise the Service Worker could be stopped in the middle of your processing:
在Service Worker中的push
事件侦听器内部,我们需要向用户显示通知,并告诉事件等待浏览器显示该事件,然后函数才能终止。 我们延长事件的生存期,直到浏览器完成显示通知为止(直到兑现承诺已解决),否则服务工作者可能会在处理过程中停止:
self.addEventListener('push', (event) => {
const promiseChain = self.registration.showNotification('Hey!')
event.waitUntil(promiseChain)
})
More on notifications in the Notifications API Guide.
《 通知API指南》中有关通知的更多信息。
jira推送api