Как стать автором
Обновить

Рассылка Push-уведомлений с SpringBoot сервера

Время на прочтение 4 мин
Количество просмотров 19K

Предисловие


Приветствую Вас. Недавно передо мной стала задача — настроить Push-уведомления на сайте. С этим я столкнулся впервые и во много разобраться мне помогла эта статья. В ней же уже есть описание серверной стороны, но, в процессе изучения данной темы я обнаружил более удобный способ реализации средствами самой библиотеки Firebase. Собственно, о нем я и хотел бы вам рассказать, т.к. внятного объяснения в интернете мне не удалось найти.

Также данная статья может быть полезна программирующим на Node.js, Python и Go, поскольку библиотека есть и на этих языках.

Непосредственно к сути


В данной статье я расскажу только о серверной стороне.
(клиентскую часть Вы можете настроить используя ту самую статью)
Итак:

  • Для начала Вам нужно зайти на сайт, зарегистрировать и создать проект.
  • Далее в левом верхнем углу нажимаем на шестерню и выбираем «Настройки проекта».
  • Переходим на вкладку «Сервисные аккаунты», выбираем интересующий нас язык, нажимаем на «создание закрытого ключа» и скачиваем сгенерированный файл
Данный JSON-файл содержит в себе конфигурацию необходимую для библиотеки Firebase.
Теперь займемся сервером

Для удобства объявим в application.properties путь к скаченному файлу

fcm.service-account-file = /path/to/file.json

Добавим необходимые зависимости в pom.xml

<dependency>
    <groupId>com.google.firebase</groupId>
    <artifactId>firebase-admin</artifactId>
    <version>6.7.0</version>
</dependency>

Создадим бин возвращающий наш JSON:

@ConfigurationProperties(prefix = "fcm")
@Component
public class FcmSettings {
    private String serviceAccountFile;

    public String getServiceAccountFile() {
        return this.serviceAccountFile;
    }

    public void setServiceAccountFile(String serviceAccountFile) {
        this.serviceAccountFile = serviceAccountFile;
    }
}


Конфиг-объект

@Getter 
@Setter
public class PushNotifyConf {

    private String title;
    private String body;
    private String icon;
    private String click_action;
    private String ttlInSeconds;

    public PushNotifyConf() {
    }

    public PushNotifyConf(String title, String body, String icon, 
                                    String click_action, String ttlInSeconds) {
        this.title = title;
        this.body = body;
        this.icon = icon;
        this.click_action = click_action;
        this.ttlInSeconds = ttlInSeconds;
    }
}

Поля:
  • title — Оглавление уведомления
  • body — текст уведомления
  • icon — ссылка на картинку
  • click_action — ссылка, куда отправится пользователь при клике на уведомление (с названием, пример в сервисе)
    Их можно добавлять несколько, но все отобразит не каждый браузер (ниже пример из Chroma)
  • ttlInSeconds — время актуальности уведомления


И сервис, в котором и будет вся логика отправки уведомлений:

@Service
public class FcmClient {


    public FcmClient(FcmSettings settings) {
        Path p = Paths.get(settings.getServiceAccountFile());
        try (InputStream serviceAccount = Files.newInputStream(p)) {
            FirebaseOptions options = new FirebaseOptions.Builder()
                  .setCredentials(GoogleCredentials.fromStream(serviceAccount))
                  .build();

            FirebaseApp.initializeApp(options);
        } catch (IOException e) {
            Logger.getLogger(FcmClient.class.getName())
                    .log(Level.SEVERE, null, e);
        }
    }

    public String sendByTopic(PushNotifyConf conf, String topic)
            throws InterruptedException, ExecutionException {
       
        Message message = Message.builder().setTopic(topic)
                .setWebpushConfig(WebpushConfig.builder()
                        .putHeader("ttl", conf.getTtlInSeconds())
                        .setNotification(createBuilder(conf).build())
                        .build())
                .build();

        String response = FirebaseMessaging.getInstance()
                .sendAsync(message)
                .get();
        return response;
    }

    public String sendPersonal(PushNotifyConf conf, String clientToken)
            throws ExecutionException, InterruptedException {
        Message message = Message.builder().setToken(clientToken)
                .setWebpushConfig(WebpushConfig.builder()
                        .putHeader("ttl", conf.getTtlInSeconds())
                        .setNotification(createBuilder(conf).build())
                        .build())
                .build();

        String response = FirebaseMessaging.getInstance()
                .sendAsync(message)
                .get();
        return response;
    }

    public void subscribeUsers(String topic, List<String> clientTokens)
            throws  FirebaseMessagingException {
        for (String token : clientTokens) {
             TopicManagementResponse response = FirebaseMessaging.getInstance()
                    .subscribeToTopic(Collections.singletonList(token), topic);
        }
    }

    private WebpushNotification.Builder createBuilder(PushNotifyConf conf){
        WebpushNotification.Builder builder = WebpushNotification.builder();
        builder.addAction(new WebpushNotification
                .Action(conf.getClick_action(), "Открыть"))
                .setImage(conf.getIcon())
                .setTitle(conf.getTitle())
                .setBody(conf.getBody());
        return builder;
    }
}

Я: — Firebase, почему так много билдим?
Firebase: — Потому что
  1. Конструктор служит для инициализации FirebaseApp с использованием нашего JSON-файла
  2. Метод sendByTopic() производит отправку уведомлений пользователям подписанным на заданную тему.
  3. Метод subscribeUsers() подписывет на тему (topic) пользователей (clientTokens).
    может выполняться асинхронно, для этого используется .subscribeToTopicAsync()

  4. Метод sendPersonal() реализует отправку персонального уведомления пользователю (clientToken)
  5. Метод createBuilder() создает сообщение

Результат

image

Другой браузер

image
Иконок нет потому, что Ubuntu:)

Подводим итоги



По сути, библиотека Firebase собирает нам JSON примерно такого вида:

{from: "Server key"
​
     notification: {
​​        title: "Привет Habr"
        actions: (1) [​​​
               0: Object { 
                       action: "https://habr.com/ru/top/",
                       title: "Открыть" }
                           ]​​
        length: 1​​
        body: "как-то так"​​
        image: "https://habrastorage.org/webt/7i/k5/77/7ik577fzskgywduy_2mfauq1gxs.png"​​
          }
​priority: "normal"}

А на стороне клиента Вы уже парсите его, как нравится.

Спасибо за внимание!

Полезные ссылки:


firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/messaging/WebpushNotification

habr.com/ru/post/321924/#otpravka-uvedomleniy-s-servera

firebase.google.com/docs/web/setup
Теги:
Хабы:
+11
Комментарии 10
Комментарии Комментарии 10

Публикации

Истории

Работа

Java разработчик
356 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн