【问题标题】:Best practice push notifications in vaadin and spring bootvaadin 和 Spring Boot 中的最佳实践推送通知
【发布时间】:2021-05-04 13:32:23
【问题描述】:

我正在做一个项目,它使用 vaadin 作为前端,使用 spring-boot 作为后端。在这个应用程序中,我有一个特定的用例,其中一组用户(操作员)在不同的站点上工作,他们可以生成事件,应该通知另一组不同的用户(监督者) - 但仅限于他们被分配到的站点。 通常没什么大不了的,但我想在有新事件发生时使用网络推送通知来通知主管。

我开始阅读一些关于Notification API 的内容,并设法在 vaadin 中创建了一个视图,用于检查浏览器是否被授予发送通知的权限(在我的 Windows 10 示例中)以及是否尚未授予权限显示一个按钮来请求它。如果已授予权限,则该按钮将被隐藏,并且标签确认已授予权限。同时发送一个测试通知(让我测试它是否有效)。 到目前为止一切正常。

我使用 java/vaadin 和本机 java 脚本的混合实现了一切

window.sendNotification = function(element, body, icon, title, type, id) {
    var options = {
        body: body,
        icon: icon
    }
    var notification = new Notification(title, options);
    notification.onclick = (e) => element.$server.notificationClicked(type, id);
}


window.checkNotificationPermission = function() {
    return Notification.permission;
}

window.askNotificationPermission = function(element) {
    function handlePermission(permission) {
        element.$server.permissionAsked(permission);
    }

    // Let's check if the browser supports notifications
    if (!('Notification' in window)) {
        console.log("This browser does not support notifications.");
    } else {
        if (checkNotificationPromise()) {
            Notification.requestPermission()
                .then((permission) => {
                    handlePermission(permission);
                })
        } else {
            Notification.requestPermission(function(permission) {
                handlePermission(permission);
            });
        }
    }
}
function checkNotificationPromise() {
    // safari supported permission or all other browsers?
    try {
        Notification.requestPermission().then();
    } catch (e) {
        return false;
    }

    return true;
}

这是 js 方面,在 java/vaadin 中,我使用 @PWA(使用生成的 sw.js 和清单)、@Push 和 @JsModule 来注释视图以包含 js 代码。另外我使用 page.executeJs() 调用每个 js 函数,例如这样:

    Page page = UI.getCurrent().getPage();
    page.executeJs("askNotificationPermission($0)", this.getElement());

我还提供了回调方法,用于处理授予权限或单击通知的时间。例如:

@ClientCallable
private void permissionAsked(String permission) {
    sendSuccessNotification();
    permissionChecked(permission);
}

直到这里一切正常。

现在我最大的问题是如何自动通知每个用户(其中​​一些甚至可能在不同的机器上登录,例如 PC 和智能手机)。我想过让另一个线程在视图上运行并让它轮询后端以获取新通知:

像这样:

@Override
protected void onAttach(AttachEvent attachEvent) {
    // Start the data feed thread
    thread = new NotificationThread();
    thread.start();
}

@Override
protected void onDetach(DetachEvent detachEvent) {
    // Cleanup
    thread.interrupt();
    thread = null;
}

private class NotificationThread extends Thread {

    @Override
    public void run() {
        try {
            while (true) {
                Thread.sleep(10000); // update every 10 seconds

                List<ResponseNotification> findNewNotifications = notificationController.findNewNotifications();
                if (findNewNotifications.size() > 1) {
                    sendGeneralNotification("You have new notifications", "New Notifications!", NavigationTarget.NOTIFICATIONS, "");
                } else if (findNewNotifications.size() == 1) {
                    sendGeneralNotification("An operator requires your attention", "Operator", NavigationTarget.OPERATOR, notification.operator.id);
                }
            }
        } catch (InterruptedException e) {
            // ...
            // handle 
        }
    }
}

这种方法的最大问题是身份验证在后端不再可用,因为它是从另一个线程调用的,因此SecurityContextHolder.getContext().getAuthentication() 将返回 null。 但我也不确定一般方法是否合理,因为视图总是需要打开的,而且我觉得每 10 秒轮询一次可能会给后端带来一些不必要的负载,一旦有多个主管。

事件存储在我的数据库中,除了通知之外,我还想发送一封电子邮件,所以如果有办法从后端发送通知并在处理事件时推送给客户端,那将是完美的. 也许有人已经有经验并且可以帮助我找到解决这个问题的方法。

提前致谢!

【问题讨论】:

  • 你检查过这个其他问题吗:stackoverflow.com/questions/67356031/…
  • @TatuLund 感谢您的提示,不,这在我的研究过程中没有出现。这可能是第一个解决方案,但它是否正确?如果 vaadin 是一个独立于后端运行并通过 rest 访问它的无状态服务会怎样。如何验证登录?
  • Vaadin Flow(不是 Fusion)不能运行“无状态”——它总是会运行一个会话,因为它保存了每个 UI 的场景图。您将始终必须在您的 UI/客户端/用户和您的事件源之间建立关系。如果你正在投票,你可以为这些人创造一个长期运行的代币。或者您可以信任您的 vaadin 服务并发送所有更新或允许轮询所有更新以提高效率,然后发送到例如用户名,例如推出自己的发布/订阅。然后当然你可以使用一些已建立的 pub/sub。没有灵丹妙药。
  • 其实Vaadin push就是使用websocket。
  • Websocket 是一个有状态的协议。 REST 是无状态的,我认为您不能使用 REST 服务推送通知。

标签: spring-boot push-notification vaadin web-push vaadin-flow


【解决方案1】:

如果您需要在应用程序未打开的情况下通知用户,您需要查看Web Push。不幸的是,这需要 Safari 上的自定义(但类似)解决方案,并且是 not available on iOS,这是 Vaadin 还没有内置解决方案的主要原因。 Marcus Hellberg made a prototype 几年前可能会提供一些提示。

要通知当前已打开应用程序的用户,请考虑这是否适用于您的情况: 与其让每个用户每 10 秒轮询一次相关事件,不如考虑“订阅”与用户感兴趣的每个站点有关的事件,然后在事件发生时向所有订阅者发送一个“事件”。用户只有在登录后才会保持订阅状态,因此发布线程不需要跟踪权限。

这需要您为发布/订阅机制使用某种事件总线,为此您可以查看Collaboration Engine - 主要是因为它与 Vaadin 集成,并带来了您可能会觉得有用的其他协作功能(也许事件日志,或每个站点的聊天,以便每个操作员可以添加一些额外的详细信息)。当您使用高级 API 和组件时,它最强大,但您可以使用低级 API 来做很多漂亮的事情。

您可以通过多种方式进行设置,例如创建一个名为“通知”的主题,使用 CollaborativeMap 共享每个站点的更新。 (或者您可以根据需要为每个站点创建一个单独的主题。)

每个用户都这样:

CollaborationEngine.getInstance().openTopicConnection(this, "notifications", localUser, topic -> {
            CollaborationMap stations = topic.getNamedMap("stations");
            return stations.subscribe(event -> {
                if (MY_STATIONS.contains(event.getKey())) {
                    Notification notification = new Notification(event.getValue(String.class));
                    notification.setDuration(5000);
                    notification.open();
                }
            });
        });

那么当有关于一个站的更新时,用同样的方法获取stationMap,但是更新它:

stationMap.put(STATION_ID,  STATION_ID + “: An update at " + new Date());

这当然接近于伪代码简介,因为这可能不是您正在寻找的内容,但代码确实产生了一个工作示例:

【讨论】:

猜你喜欢
  • 2020-03-08
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 2012-09-11
  • 1970-01-01
  • 1970-01-01
  • 2020-09-30
  • 2021-07-23
相关资源
最近更新 更多