HTML5 EventSource onmessage never called from Web API 2

I have a RESTful Web API 2 solution whose methods are called from an AngularJS app. I wanted to add support for sending notifications from the server side API to the client logic. However, the message and public methods created by calls to addEventListener in Javascript are never called. The Web API controller looks like this:

namespace Controllers
{
    public class EventController : ApiController
    {
        private static readonly List<StreamWriter> ConnectedClients = new List<StreamWriter>();
        private static Timer _timer;

        public EventController()
        {
            if(_timer == null)
                _timer = new Timer(TimerCallback, null, 0, 2000);
        }

        public static void OnStreamAvailable(Stream stream, HttpContent headers, TransportContext context)
        {
            var streamwriter = new StreamWriter(stream);
            ConnectedClients.Add(streamwriter);
        }

        private static void MessageCallback(Message m)
        {
            foreach (var subscriber in ConnectedClients)
            {
                try
                {
                    subscriber.Write("data: {0}\n\n", JsonConvert.SerializeObject(m));
                    subscriber.Flush();
                }
                catch (Exception ex)
                {
                    // This probably means the user has disconnected
                    ConnectedClients.Remove(subscriber);
                }
            }
        }

        private static void TimerCallback(object state)
        {
            var m = new Message
            {
                Date = DateTime.Now.ToUniversalTime().ToString("u"),
                Text = "Hello World!"
            };
            MessageCallback(m);
        }

        [HttpGet]
        public HttpResponseMessage Get(HttpRequestMessage request)
        {
            // New subscription request
            var response = request.CreateResponse();
            response.Headers.Add("Access-Control-Allow-Origin", "*");
            response.Headers.Add("Cache-Control", "no-cache, must-revalidate");
            response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>)OnStreamAvailable, "text/event-stream");
            return response;
        }
    }
}

      

Note that I also tried the following for my HttpGet method:

public HttpResponseMessage Get()
{
    // New subscription request
    var response = Request.CreateResponse();
    response.Headers.Add("Access-Control-Allow-Origin", "*");
    response.Headers.Add("Cache-Control", "no-cache, must-revalidate");
    response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>)OnStreamAvailable, "text/event-stream");
    return response;
}

      

Javascript:

if (!!window.EventSource) {
    var eventSource = new EventSource(GlobalConfig.apiRoot + 'Event');

    eventSource.addEventListener('open', function (e) {
        console.log("open");
    }, false);

    eventSource.addEventListener('error', function (e) {
        console.log("error");
    }, false);

    eventSource.addEventListener('message', function (e) {
        console.log('message');
    }, false);

} else {
    // not supported!
}

      

I am running this through IISExpress in Visual Studio 2013. However, I also tested in IIS 8 with the same result. All tests were performed in version 36.0.1985.143 m Google Chrome. I can check:

  • The Get method is called on the frontend.
  • The timer (which I am setting up just for testing) starts up and calls the TimerCallback method as expected. Then the MessageCallback runs.
  • If I close Visual Studio / IISExpress with Chrome, the error method opens in Javascript with the net :: ERR_CONNECTION_REFUSED error.
  • Likewise, if I shutdown the browser using Visual Studio / IISExpress throwing an HttpException is thrown in the controller's MessageCallback method.

That is, there seems to be some kind of connection, but I can't figure out why the message and open handlers are never called in Javascript.

To check that nothing in AngularJS was affecting things, I moved my Javascript to a static HTML page. The following was logged in the Chrome console:

Request URL:http://localhost/.../api/Event
Request Headers
Provisional headers are shown
Accept:text/event-stream
Cache-Control:no-cache
Referer:http://localhost/.../test.html
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)    Chrome/36.0.1985.143 Safari/537.36

      

The status of the request is always pending. However, given that the connection remains open, this is what I would expect.

Any suggestions for getting this functionality will be gratefully received.

+3


source to share


1 answer


I think the correct formatting is:

subscriber.Write("event: message\n");
subscriber.Write("data: {0}\n\n");

      



The event listener only responds to events containing the name of the event message (in this case message

). This means that the server can send several types of events, which can be signed separately.

0


source







All Articles