In my recent post, we have seen how to create basic chart using jQuery Flot and ASP.NET Web API. In this article, we’ll create a chart to display real-time updates using native HTML5 push notification. The HTML5 Server-Sent event (SSE) model allows you to push real-time data updates from the server to the browser.
Generally, we are using Ajax methods to get new data at specified intervals. So, each time client has to call the server even there is no new data. It’s not efficient. With HTML5 Server-Sent requests, we can stream data from the server, with updates pushed from there without the code at client side having to keep requesting it and we can process new data in javascript code when it receives from the server.
Web API:
Here, our objective is to get random number at random time interval from server and plot the chart. We are going to start with Henrik F Nielsen’s post to develop an application utilizing a real HTTP push messaging infrastructure. We are defining content-type : text/event-stream and data in following format for SSE:
data: Hello World!\n
\n
There is no length limit for the text and empty line fires the event. We will generate random number instead of “Hello World” text to draw chart.
public class ChartController : ApiController { private static readonly Lazy<Timer> _timer = new Lazy<Timer>(() => new Timer(TimerCallback, null, 0, 1000)); private static readonly ConcurrentQueue<StreamWriter> _streammessage = new ConcurrentQueue<StreamWriter>(); public HttpResponseMessage Get(HttpRequestMessage request) { Timer t = _timer.Value; HttpResponseMessage response = request.CreateResponse(); response.Content = new PushStreamContent(OnStreamAvailable, "text/event-stream"); return response; } private static void TimerCallback(object state) { Random randNum = new Random(); foreach (var data in _streammessage) { data.WriteLine("data:" + randNum.Next(30, 100) + "\n"); data.Flush(); } //To set timer with random interval _timer.Value.Change(TimeSpan.FromMilliseconds(randNum.Next(1,3)*500), TimeSpan.FromMilliseconds(-1)); } public static void OnStreamAvailable(Stream stream, HttpContentHeaders headers, TransportContext context) { StreamWriter streamwriter = new StreamWriter(stream); _streammessage.Enqueue(streamwriter); } }
Here we are changing timer interval randomly to create more real environment.
View:
For simplicity, we are taking same app to consume web api, create new action in Home controller, add view without layout.
Download Flot and add jquery.flot.min.js, excanvas.min.js files from the downloaded folder, Make sure jquery is already included before float js file.
<!--[if lte IE 8]><script language="javascript" type="text/javascript" src="~/Scripts/excanvas.min.js"></script><![endif]--> <script language="javascript" type="text/javascript" src="~/Scripts/jquery.flot.min.js"></script>
HTML:
<div style="text-align: center"> <h1> Trade Price: <span id="priceHolder"></span> </h1> <div id="placeholder" style="width: 600px; height: 300px; margin: 0 auto"> </div> </div>
placeholder is used for chart and priceHolder is to display current price.
EventSource API:
To start receiving server notifications, we simply open a connection to an SSE URI and setup our callbacks in javascript:
if (!!window.EventSource) { var source = new EventSource('http://localhost:49999/api/chart/'); source.addEventListener('message', function (e) { console.log(e.data); update(parseInt(e.data, 10)); }, false); source.addEventListener('open', function (e) { console.log("open!"); }, false); source.addEventListener('error', function (e) { if (e.readyState == EventSource.CLOSED) { console.log("error!"); } }, false); } else { // not supported! }
Chart:
When we get new number from server side, the first number is removed, new number is pushed, the points are re-indexed and assigned to Flot chart.
var ypt = [], totalPoints = 30; function initData() { for (var i = 0; i < totalPoints; ++i) ypt.push(0); return getPoints(); } function getData(data) { if (ypt.length > 0) ypt = ypt.slice(1); ypt.push(data); return getPoints(); } function getPoints() { var ret = []; for (var i = 0; i < ypt.length; ++i) ret.push([i, ypt[i]]); return ret; } // setup plot var options = { series: { shadowSize: 0, bars: { show: true, barWidth: 0.75, fill: 1 } }, // drawing is faster without shadows yaxis: { min: 0, max: 100, tickFormatter: function (val, axis) { return '$' + val; } }, xaxis: { show: false } }; var plot = $.plot($("#placeholder"), [initData()], options); function update(data) { $('#priceHolder').text('$' + data); plot.setData([getData(data)]); plot.draw(); }
You can download source code from Github.
Hope, you enjoy it. Share your opinion or suggestion how you are displaying real-time updates.
Reference:
Recent ASP.NET Web API Updates – April 24
Native HTML5 push notifications with ASP.NET Web API and Knockout.js
Thanks. This was very useful. I do have a question. I tried consuming the API stream from within the API contoller application, it works fine and able to plot the graph. However, if I try to cnsume it from outside of the API controller application, the chart cannot be drawn. I think I am runing into an issue that is outlined in the article
http://www.html5rocks.com/en/tutorials/eventsource/basics/
According to the above article,
JavaScript API
To subscribe to an event stream, create an EventSource object and pass it the URL of your stream:
“If the URL passed to the EventSource constructor is an absolute URL, its origin (scheme, domain, port) must match that of the calling page”. So my question is what if the calling page does not match the URL origin? I think this is the cause of the problem I am running into. Any ideas on how to call the URL if the client page consuming the data is outside of the API controller application? Please advise.
Maybe try
response.setHeader(“Access-Control-ALlow-Origin”, “*”)
allowing cross site resource access.
Very cool example, tanks :)
Can it be so that the chart is getting updated.. rather than the old data disappearing… like a time based chart…??
Nice work!!.
Thanks Jalpesh !!!
For SignalR, Please see my post: http://techbrij.com/1039/jquery-flot-signalr-asp-net-mvc-chart
For the time being, you’d be further ahead to use a library like signalr for push notifications. http://caniuse.com/eventsource shows that there are some gaps in support of server side events.
who can help me code share on Facebook,tiwtter.thanks
Thanks, will this support all the browsers? Isn’t SignalR made to do the same thing?
Good example. I have no comment on the technical side but to make the example look more realistic you could make the next price dependant on the last price. So p = p + (p*random) where random is between -.2 and +.2. There actually have been various studies done that indicate this is how stock prices behave, its called the Random Walk Hypothesis.
Its really good. Congratulation for getting an MVP certification provided by Microsoft
This is very good.Can we do the same thing when there is an insert/update to the database table record?
It’s considered a common practice to reference the sources… 80% of the code is copied from here http://www.strathweb.com/2012/05/native-html5-push-notifications-with-asp-net-web-api-and-knockout-js/
Even the port (49999) and console.log are exactly the same…
Actually even the typos (double exclamation mark) are the same…