How to use D3.js for Server Side Chart Generation in .NET (Part 3)

How to use D3.js for Server Side Chart Generation in .NET

Part 1: Creating a Donut Chart using D3.js
Part 2: Render/Export D3.js Chart as PNG Image using PhantomJS
Part 3: Creating a simple REST Service using ASP.NET Web API

In order to make our Chart Generation logic reusable I have decided to put it in a simple RESTful Service that will be implemented using ASP.NET Web API. We need to take all files that we created in previous parts of this series and place it in one temporary folder that will be used for Chart Generation.

I have decided to use C:\Temp\Chart folder,  and have copied following content:

donut_template.html
phantomjs.exe
render.js

As you can see, phantomjs.exe and render.js are files that we used in Part 2 of our series. donut_template.html is actually donut.html file from the Part 1 with one following change:
I have removed the explicit definition of our dataset used for rendering of Donut Chart:

var data = [{ label: 'Here', value: 190000, color: "#1e398f" },
            { label: 'Some', value: 50000, color: "#f7a600" },
            { label: 'Labels', value: 420000, color: "#e17e62" },
            { label: 'Needed', value: 1530000, color: "#985181" }];

With following line of code:
var data = $$$PAYLOADINJECTION$$$;

This line will be used later in REST Service for Injecting the JSON dataset from our Server Side Model.

Now we can focus on creating a REST Service using ASP.NET Web API.
To do that, do the following steps:

  1. Start new ASP.NET Web Application Project in Visual Studio and chose Empty Template with Web API added core references
  2. Add new Model, name it ChartModel and add following definitions:
    namespace ChartService.Models
    {
        public class ChartModel
        {
            public string label { get; set; }
            public int value { get; set; }
            public string color { get; set; }
        }
    }
  3. Now add new Controller, name it ChartController and place following code inside:
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Web.Hosting;
    using System.Web.Http;
    using Newtonsoft.Json;
    using ChartService.Models;
     
    namespace ChartService.Controllers
    {
        public class ChartController : ApiController
        {
            public HttpResponseMessage Post([FromBody] IEnumerable<ChartModel> payload)
            {
                String imageFile = GenerateChartImage(payload);
                MemoryStream imageStream = ReadChartImage(imageFile);
                
                var result = new HttpResponseMessage(HttpStatusCode.OK);
                result.Content = new ByteArrayContent(imageStream.ToArray());
                result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
     
                return result;
            }
     
            private string GenerateChartImage(IEnumerable<ChartModel> payload)
            {
                Guid chartID = Guid.NewGuid();
     
                string templatePath = @"C:\Temp\Chart\donut_template.html";
                string htmlPath = String.Format(@"C:\Temp\Chart\{0}.html", chartID);
                string pngPath = String.Format(@"C:\Temp\Chart\{0}.png", chartID);
     
                // Read HTML Template File
                string html = File.ReadAllText(templatePath);
     
                // Create copy with Payload
                html = html.Replace("$$$PAYLOADINJECTION$$$", JsonConvert.SerializeObject(payload));
                File.WriteAllText(htmlPath, html);
     
                // Start PhantomJS as Hidden Process in Background
                ProcessStartInfo startInfo = new ProcessStartInfo();
                startInfo.CreateNoWindow = false;
                startInfo.UseShellExecute = false;
                startInfo.FileName = Path.Combine(@"C:\Temp\Chart\", "phantomjs.exe");
                startInfo.WorkingDirectory = @"C:\Temp\Chart\";
                startInfo.WindowStyle = ProcessWindowStyle.Hidden;
                startInfo.Arguments = "render.js \"" + htmlPath + "\" \"" + pngPath + "\"";
     
                try
                {
                    using (Process phantomJsProc = Process.Start(startInfo))
                    {
                        phantomJsProc.WaitForExit();
                        return pngPath;
                    }
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    // At the end, don't forget to delete temp. generated files
                    File.Delete(htmlPath);
                }
            }
     
            private static MemoryStream ReadChartImage(string imageFile)
            {
                FileStream fileStream = new FileStream(imageFile, FileMode.Open);
                Image image = Image.FromStream(fileStream);
                MemoryStream memoryStream = new MemoryStream();
                image.Save(memoryStream, ImageFormat.Png);
                
                // Dispose objects and delete the temp. generated files
                image.Dispose();
                fileStream.Close();
                File.Delete(imageFile);
     
                return memoryStream;
            }
        }
    }

Now with this Implementation of ChartController.cs we can simply POST a JSON Payload containing our dataset that will be injected into our donut_template.html file.

After that we will run render.js on PhantomJS as hidden background process on our new generated html file and generate the PNG Image file that will be delivered in response to our http request as “image/png” content type.

You can easily test the functionality of code in Fiddler by composing the POST request to your new REST Service:

fiddler_request

By Executing your request you should get following response of your request:

fiddler_result

Now we have a nice REST Web API Service that uses D3.js Chart Library for Server Side generating, pre-rendering and delivering of Charts Images.

Now you can create a ASP.NET Web Application that will use the new REST Service to dynamically generate and display the Chart.


Posted Jul 30 2015, 02:31 PM by Armin Kalajdzija

Comments

Steve Jones wrote re: How to use D3.js for Server Side Chart Generation in .NET (Part 3)
on 09-22-2016 18:36

This is fantastic! Thanks for blogging this.

developers.de is a .Net Community Blog powered by daenet GmbH.