Search code examples
asp.netvisual-studiovisual-studio-2019razor-pagesasp.net-apicontroller

Updating values of a chart in a razor page


I created an Asp Net Razor project in visual studio 2019.

I'm learning the technology with some exercises. Specifically, I would like to display on a page a chart whose data i would like to retrieve via an ApiController.

   [Route("api/[controller]")]
    [ApiController]
    public class AjaxAPIController : ControllerBase
    {
        public ChartData chartData = new ChartData();

        [HttpGet]
        [Route("ChartDataJson")]
        public IActionResult ChartDataJson()
        {
            // First example
            var x = new List<double> { 1.0,};
            var y = new List<double> { 10.0,};
            var chartData = new { x, y };
            return Ok(chartData);
        }

        [HttpGet]
        [Route("ChartDataJson2")]
        public IActionResult ChartDataJson2()
        {
            // Second Example
            var x = new List<double> {1.0, 20.0, 30.0 };
            var y = new List<double> {2.0, 30.0,  45.0};
            var chartData = new { x, y };
            return Ok(chartData);
        }

        [HttpPost]
        [Route("UpdateChartData")]
        public IActionResult UpdateChartData([FromBody] ChartData data)
        {
            // Read received data
            var newDataX = data.X; 
            var newDataY = data.Y;
            var chartData = new { x = new List<double>[] { newDataX }, y = new List<double>[] { newDataY } };
            return Ok(chartData);
        }
    }

The values of the chart, can be updated in 3 different ways:

  1. Click button 1: update the values of the chart with fixed values; (Get)
 //  When the button 1 is clicked, get  data from the controller and update the chart
        $("#btnGet").click(function () {
            console.log("button 1 click is executed.");
            $.get("/api/AjaxAPI/ChartDataJson", function (data) {
                updateScatterChart(data);
            });
        });
  1. Click button 2: same as point 1 but with different values; (Get)
         //  When the button 2 is clicked, get  data from the controller and update the chart
        $("#btnGet2").click(function () {
            console.log("button 2 click is executed.");
            $.get("/api/AjaxAPI/ChartDataJson2", function (data) {
                updateScatterChart(data);
            });
        });
  1. Call post: when I receive from outside a post call with some data in json format. (Post)
        // Update the data chart when a post request is received
        $.post("/api/AjaxAPI/UpdateChartData", function (data) {
            console.log("X values:", data.x);
            console.log("Y values:", data.y);
            updateScatterChart(data);
        });

The result is something like:

Application

Steps 1 and 2 work correctly but step 3 doesn't work...

The first problem occurs when the page is loaded:

jquery.min.js:2 POST https ://localhost:44354/api/AjaxAPI/UpdateChartData 400 (Bad Request)

In addition, if I try to launch in the browser console the script:

fetch("https://localhost:44354/api/AjaxAPI/UpdateChartData", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ X: [400.0], Y: [600.0] }),
})
  .then((response) => response.json())
  .then((data) => console.log(data))
  .catch((error) => console.error("Error: " + error));

to simulate the post request, I can in debug read the values in the ApiController class but I can't correctly update the values of the chart.

My full code is available here: https://github.com/DanielScerpa/RazorPageChart/tree/dev_chart

Summarizing:

I would like to understand how I can correctly update my chart values when I receive a post request and I would like to understand the startup problem 400 (Bad Request)

!!!...EDIT...!!!

Summarazing:

  1. How can I automatically update the chart when I receive a post call in the apicontroller?

Example, someone calls:

 <https://localhost:44354/api/AjaxAPI/UpdateChartData>

I intercept the post call via the code:

    [HttpPost]
        [Route("UpdateChartData")]
        public IActionResult UpdateChartData([FromBody] ChartData data)
        {
            // Read received data
            var newDataX = data.X; 
            var newDataY = data.Y;
            var chartData = new { x = new List<double>[] { newDataX }, y = new List<double>[] { newDataY } };
            return Ok(chartData);
        }
    }

at this point how can I update the chart with the newly retrieved values (data.x and data.y)?

  1. How can I solve 404 bad request error at startup?

Solution

  • From your requirement, you are expecting the web application to be updated for the chart when any user posts the request to the "UpdateChartData" API.

    This requires the SignalR for the real-time communication. The full demo for the installation and tutorial you may refer to the link provided.

    As you are using the .NET Core 2.2 for the project and this version is deprecated, the only supported version for Microsoft.AspNetCore.SignalR.Common is with version 1.1.0. The provided steps below are for setting up the SignalR in your application specified for .NET Core 2.2.

    1. Via NuGet, install Microsoft.AspNetCore.SignalR.Common 1.1.0

    2. Right-click the project > Manage Client-side Libraries, it will generate a libman.json file. In the file, you install the @microsoft/signalr library as below:

    {
      "version": "1.0",
      "defaultProvider": "unpkg",
      "libraries": [
        {
          "library": "@microsoft/signalr@latest",
          "destination": "wwwroot/js/signalr",
          "files": [
            "dist/browser/signalr.js",
            "dist/browser/signalr.min.js"
          ]
        }
      ]
    }
    
    1. Create a SignalR hub.
    namespace Razor_WebAPI_Application.Hubs
    {
        public class ChartHub : Hub
        {
            public async Task UpdateChart(ChartData chartData)
            {
                await Clients.All.SendAsync("UpdateChart", chartData);
            }
        }
    }
    
    1. Configure SignalR in Startup.cs
    public class Startup 
    {
        public void ConfigureServices(IServiceCollection services)
        {
            ...
            services.AddSignalR();
        }
    
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            ...
    
            app.UseSignalR(routes =>
                {
                    routes.MapHub<ChartHub>("/chartHub");
                });
        }
    }
    
    1. Get the ChartHub from the DI. And emit the data to all clients.
    using Microsoft.AspNetCore.SignalR;
    using Razor_WebAPI_Application.Hubs;
    
    [Route("api/[controller]")]
    [ApiController]
    public class AjaxAPIController : ControllerBase
    {
    
        private readonly IHubContext<ChartHub> _hubContext;
    
        public AjaxAPIController(IHubContext<ChartHub> hubContext)
        {
            _hubContext = hubContext;
        }
    }
    
    [HttpPost]
    [Route("UpdateChartData")]
    public IActionResult UpdateChartData([FromBody] ChartData data)
    {
        // Read received data
        var newDataX = data.X;
        var newDataY = data.Y;
        var chartData = new { x = new List<double>[] { newDataX }, y = new List<double>[] { newDataY } };
    
        _hubContext.Clients.All.SendAsync("UpdateChart", chartData);
    
        return Ok(chartData);
    }
    
    1. Add SignalR client code. With this, you wait for the SignalRHub to emit the "UpdateChart" event and update the chart but not send the POST request to the "UpdateChartData" API that requires the request body.
    <script src="~/js/signalr/dist/browser/signalr.js"></script>
    
    <script type="text/javascript">
    
      ...
      var connection = new signalR.HubConnectionBuilder().withUrl("/chartHub").build();
    
      connection.on("UpdateChart", function (data) {
          updateScatterChart(data);
      });
    
      connection.start().then(function () {
    
      }).catch(function (err) {
          return console.error(err.toString());
      });
    </script>
    

    enter image description here