LINUX.ORG.RU

Получать сообщения с сервера в реальном времени - авторизация

 


0

1

Есть такой код на бэкенде

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Http;
    
    using Services;
    using Models;
    
    [Route("api/[controller]")]
    [ApiController]
    public class FeedbackController : ControllerBase
    {
        public FeedbackController(FeedbackService feedbackService)
        {
            this.FeedbackService = feedbackService;
        }
        
        private FeedbackService FeedbackService { get; set; }
        
        // ...
        
        [Route("[action]")]
        [Authorize]
        [HttpGet]
        public async Task Push(CancellationToken cancellationToken)
        {
            Response.StatusCode = 200;
            Response.Headers.Add("Content-Type", "text/event-stream");

            EventHandler<FeedbackEventArgs> onFeedbackReceived = async (sender, eventArgs) =>
            {
                try
                {
                    var newMessageCount = eventArgs.NewMessageCount;
                    await Response.WriteAsync($"data:{newMessageCount}\n\n");
                    await Response.Body.FlushAsync();
                }
                catch (Exception ex)
                {
                    // Log the exception
                    Program.Logger.Error(ex, ex.Message);
                }
            };

            this.FeedbackService.Subscribe(onFeedbackReceived);

            while (!cancellationToken.IsCancellationRequested)
            {
                await Task.Delay(1000);
            }

            this.FeedbackService.Unsubscribe();
        }
    }
FeedbackService:
        // ...
        private NotificationService NotificationService;
        
        // ...
        
        public void Subscribe(EventHandler<FeedbackEventArgs> onFeedbackReceived)
        {
            this.NotificationService.Subscribe(this.CurrentLogin, onFeedbackReceived);
        }
        
        public void Unsubscribe()
        {
            this.NotificationService.Unsubscribe(this.CurrentLogin);
        }

        public void TriggerNewMessageNotification(string receiverLogin, int newMessageCount)
        {
            FeedbackEventArgs args = new FeedbackEventArgs { NewMessageCount = newMessageCount };
            this.NotificationService.Notify(receiverLogin, args);
        }
        // ...
FeedbackEventArgs:
    using System;
    
    public class FeedbackEventArgs : EventArgs
    {
        public int NewMessageCount { get; set; }
    }
Я хочу его использовать вот так на фронтенде
class Notifier {
    
    constructor() {
        store.subscribe(this.onAppStateChange.bind(this))
    }
    
    _callbacks = []
    _timerId = null
    _stage = ''
    _evtSource = null
    _sound = null
    
    // ...

    connect() {
        try {
            this._evtSource = new EventSource('/api/Feedback/Push');
            this._evtSource.addEventListener("arrived", this.onMessageArrived.bind(this));
        }
        catch (err) {
            console.log('Браузер пользователя не поддерживает Server sent events.')
        }
        // Backup method if PUSH doesn't work, we'll check for notifications at least every half hour
        this._timerId = setInterval(this.checkForNotifications.bind(this), notifierCheckingDelay)
    }

    
    disconnect() {
        clearInterval(this._timerId)
        try {
            this._evtSource.close()
        }
        catch (err) {
            console.log(
                'Браузер пользователя не поддерживает Server sent events.'
            )
        }
    }
    
    // ...
    
}
Но так при connect() получаю от сервера
HTTP/1.1 401 Unauthorized
X-Powered-By: Express
connection: close
date: Tue, 05 Mar 2024 13:28:07 GMT
server: Kestrel
content-length: 0
www-authenticate: Bearer

UPD

npm i eventsource
Поправил JS так
import EventSource from 'eventsource';
// ...
    connect() {
        try {
            let token = api.getAUTHToken();
            this._evtSource = new EventSource('/api/Feedback/Push', {
                headers: {
                    Authorization: `Bearer ${token.text}`
                }
            });
            this._evtSource.addEventListener("arrived", this.onMessageArrived.bind(this));
        }
        catch (err) {
            console.log('Браузер пользователя не поддерживает Server sent events.')
        }
        // Backup method if PUSH doesn't work, we'll check for notifications at least every half hour
        this._timerId = setInterval(this.checkForNotifications.bind(this), notifierCheckingDelay)
    }
И C# так
        [Route("[action]")]
        [Authorize]
        [HttpGet]
        public async Task Push(CancellationToken cancellationToken)
        {
            Response.StatusCode = 200;
            Response.Headers.Add("Content-Type", "text/event-stream");
            Response.Headers.Add("Cache-Control", "no-transform");

            EventHandler<FeedbackEventArgs> onFeedbackReceived = async (sender, eventArgs) =>
            {
                try
                {
                    var newMessageCount = eventArgs.NewMessageCount;
                    await Response.WriteAsync($"data:{newMessageCount}\n\n");
                    await Response.Body.FlushAsync();
                }
                catch (Exception ex)
                {
                    // Log the exception
                    Program.Logger.Error(ex, ex.Message);
                }
            };

            this.FeedbackService.Subscribe(onFeedbackReceived);

            while (!cancellationToken.IsCancellationRequested)
            {
                await Task.Delay(1000);
            }

            this.FeedbackService.Unsubscribe();
        }
Запрос выполняется бесконечно, как и положено.

★★★

Последнее исправление: damix9 (всего исправлений: 1)

Похоже «часовой пояс» на сервере UTC+0. А тебе надо поставить такой же как у юзера в браузере. Или передавай из браузера на сервер часовой пояс. Или сделай UTC+3 на сервере. При условии, что у тебя все пользователи живут в московском поясе)

romanlinux ★★★
()
Ответ на: комментарий от romanlinux

Серьезно? Такое может быть? Не исключено, но мне бы сначала вообще авторизоваться, т.е. хедер соответствующий передать. У всех юзеров точно никогда не будет одного часового пояса.

Или передавай из браузера на сервер часовой пояс.

А такое что существует?

damix9 ★★★
() автор топика

Если отредактировать так

const EventSource = require('eventsource');
// ...
    connect() {
        try {
            let token = api.getAUTHToken();
            this._evtSource = new EventSource('/api/Feedback/Push', {
                headers: {
                    Authorization: `Bearer ${token.text}`
                }
            });
            this._evtSource.addEventListener("arrived", this.onMessageArrived.bind(this));
        }
        catch (err) {
            console.log('Браузер пользователя не поддерживает Server sent events.')
        }
        // Backup method if PUSH doesn't work, we'll check for notifications at least every half hour
        this._timerId = setInterval(this.checkForNotifications.bind(this), notifierCheckingDelay)
    }

Получаю в выводе фронтенда

Proxy error: Could not proxy request /api/Feedback/Push from localhost:3000 to http://127.0.0.1:5000.
See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNRESET).

damix9 ★★★
() автор топика

а SSE разве не отменили?

Syncro ★★★★★
()
Ответ на: комментарий от romanlinux

Оказалось, на бэке надо добавить

Response.Headers.Add("Cache-Control", "no-transform");

damix9 ★★★
() автор топика
Ответ на: комментарий от KivApple

Веб-сокеты - из пушки по воробьям для такой задачи. Тут надо просто получать с сервера количество непрочитанных сообщений в чате, когда приходит новое сообщение.

damix9 ★★★
() автор топика
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.