.Net Core 2.1 Web API returns a JSON but datatable cannot consume it. Why?

.Net Core 2.1 Web API returns a JSON but datatable cannot consume it. Why?

Sammy1Sammy1 Posts: 13Questions: 4Answers: 0

How come datatable cannot consume json returned from .Net Core 2.1 Web API?
Here is my .net code snippet.

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
[Produces("application/json")]
public IActionResult Get()
{
string s = @{ "data"": [[""Tiger Nixon"",""System Architect"", ""Edinburgh"", ""5421"",""2011/04/25"",""$320,800""],[""Garrett Winters"",""Accountant"",""Tokyo"", ""8422"",""2011/07/25"",""$170,750""]]}";
JObject.Parse(s).ToString(Newtonsoft.Json.Formatting.Indented);

        return Ok(s);
    }

What is wrong??? I've spent more than 4 hours trying to figure this out but to no avail. However when I access the web api in the browser it returns the json.

PLEASE HELP!!!!

Answers

  • Sammy1Sammy1 Posts: 13Questions: 4Answers: 0

    There is quote after the @ but this forum is not showing it

  • Sammy1Sammy1 Posts: 13Questions: 4Answers: 0

    Here is the updated code. But it's still NOT working.
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
    // GET api/values
    [HttpGet]
    public ActionResult Get()
    {

            string s = "{\"data\": [[\"Tiger Nixon\",\"System Architect\", \"Edinburgh\", \"5421\",\"2011/04/25\",\"$320,800\"],[\"Garrett Winters\",\"Accountant\",\"Tokyo\", \"8422\",\"2011/07/25\",\"$170,750\"]]}";
            return  Ok(s);
    
    
        }
    
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    edited May 2019

    It looks like you are returning a string - not JSON. You could decode that string into a JSON object on the client-side, but really you'd be better just sending plain JSON.

    I'm no expert with .NET Newtonsoft controllers, but try:

    return Ok( JObject.Parse(s) );
    

    Allan

  • Sammy1Sammy1 Posts: 13Questions: 4Answers: 0
    edited May 2019

    Allan,
    Thanks for the quick response. Still doesn't work.
    Here are my server side snippet and html snippet. Any help will be greatly appreciated!


    server side web api

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;
    
    namespace jsonit.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class ValuesController : ControllerBase
        {
            // GET api/values
            [HttpGet]
            public ActionResult<IEnumerable<string>> Get()
            {
                string s = "{\"data\":[[\"Tiger Nixon\",\"System Architect\", \"Edinburgh\", \"5421\",\"2011/04/25\",\"$320,800\"],[\"Garrett Winters\",\"Accountant\",\"Tokyo\", \"8422\",\"2011/07/25\",\"$170,750\"]]}";
    
                return Ok(JObject.Parse(s));
            }
    
            // GET api/values/5
            [HttpGet("{id}")]
            public ActionResult<string> Get(int id)
            {
                return "value";
            }
    
            // POST api/values
            [HttpPost]
            public void Post([FromBody] string value)
            {
            }
    
            // PUT api/values/5
            [HttpPut("{id}")]
            public void Put(int id, [FromBody] string value)
            {
            }
    
            // DELETE api/values/5
            [HttpDelete("{id}")]
            public void Delete(int id)
            {
            }
        }
    }
    

    client html

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width">
      <title>JS Bin</title>
      
      <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/dt-1.10.18/datatables.min.css"/>
     <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
      <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
    <script type="text/javascript" src="https://cdn.datatables.net/v/dt/dt-1.10.18/datatables.min.js"></script>
       <script type='text/javascript'>
    $(document).ready( function () {
      var dragSrcRow = null;  // Keep track of the source row
      var srcTable = '';  // Global tracking of table being dragged for 'over' class setting
      var rows = [];   // Global rows for #example
      var rows2 = [];  // Global rows for #example2
      
      var data =  [
        [
          "Tiger Nixon",
          "System Architect",
          "Edinburgh",
          "5421",
          "2011/04/25",
          "$320,800"
        ],
        [
          "Garrett Winters",
          "Accountant",
          "Tokyo",
          "8422",
          "2011/07/25",
          "$170,750"
        ],
        [
          "Ashton Cox",
          "Junior Technical Author",
          "San Francisco",
          "1562",
          "2009/01/12",
          "$86,000"
        ],
        [
          "Cedric Kelly",
          "Senior Javascript Developer",
          "Edinburgh",
          "6224",
          "2012/03/29",
          "$433,060"
        ],
    ];
      
    var data2 = [
          [
          "Rhona Davidson",
          "Integration Specialist",
          "Tokyo",
          "6200",
          "2010/10/14",
          "$327,900"
        ],
        [
          "Colleen Hurst",
          "Javascript Developer",
          "San Francisco",
          "2360",
          "2009/09/15",
          "$205,500"
        ],
        [
          "Sonya Frost",
          "Software Engineer",
          "Edinburgh",
          "1667",
          "2008/12/13",
          "$103,600"
        ],
        [
          "Jena Gaines",
          "Office Manager",
          "London",
          "3814",
          "2008/12/19",
          "$90,560"
        ],
    ]
        var url = 'http://localhost:16582/api/values';
      var table = $('#example').DataTable({
     
    
       'ajax': {
          type: 'Get',
          'url': url,
          'data': function (d) {
            console.log(d.order);
            return JSON.stringify( d );
          },
          "dataSrc": function (json) {
           $("#example").val(json.recordsTotal);
           return json.data;
         }
        },
    paging: true,
        
        // Add HTML5 draggable class to each row
        createdRow: function ( row, data, dataIndex, cells ) {
          $(row).attr('draggable', 'true');
        },
        
        drawCallback: function () {
          // Add HTML5 draggable event listeners to each row
          rows = document.querySelectorAll('#example tbody tr');
            [].forEach.call(rows, function(row) {
              row.addEventListener('dragstart', handleDragStart, false);
              row.addEventListener('dragenter', handleDragEnter, false)
              row.addEventListener('dragover', handleDragOver, false);
              row.addEventListener('dragleave', handleDragLeave, false);
              row.addEventListener('drop', handleDrop, false);
              row.addEventListener('dragend', handleDragEnd, false);
            });
        }
      });
      
      
      
      var table2 = $('#example2').DataTable({
        data: data2,
        paging: false,
        
        // Add HTML5 draggable class to each row
        createdRow: function ( row, data, dataIndex, cells ) {
          $(row).attr('draggable', 'true');
        },
    
        drawCallback: function () {
          // Add HTML5 draggable event listeners to each row
          rows2 = document.querySelectorAll('#example2 tbody tr');
            [].forEach.call(rows2, function(row) {
              row.addEventListener('dragstart', handleDragStart, false);
              row.addEventListener('dragenter', handleDragEnter, false)
              row.addEventListener('dragover', handleDragOver, false);
              row.addEventListener('dragleave', handleDragLeave, false);
              row.addEventListener('drop', handleDrop, false);
              row.addEventListener('dragend', handleDragEnd, false);
            });
        }  
      });
      
    function handleDragStart(e) {
      // this / e.target is the source node.
      
      // Set the source row opacity
      this.style.opacity = '0.4';
      
      // Keep track globally of the source row and source table id
      dragSrcRow = this;
      srcTable = this.parentNode.parentNode.id
    
      // Allow moves
      e.dataTransfer.effectAllowed = 'move';
      
      // Save the source row html as text
      e.dataTransfer.setData('text/plain', e.target.outerHTML);
      
    }
      
    function handleDragOver(e) {
      if (e.preventDefault) {
        e.preventDefault(); // Necessary. Allows us to drop.
      }
    
      // Allow moves
      e.dataTransfer.dropEffect = 'move'; 
    
      return false;
    }
    
    function handleDragEnter(e) {
      // this / e.target is the current hover target.  
      
      // Get current table id
      var currentTable = this.parentNode.parentNode.id
      
      // Don't show drop zone if in source table
      if (currentTable !== srcTable) {
        this.classList.add('over');
      }
    }
    
    function handleDragLeave(e) {
      // this / e.target is previous target element.
      
      // Remove the drop zone when leaving element
      this.classList.remove('over');  
    }
      
    function handleDrop(e) {
      // this / e.target is current target element.
    
      if (e.stopPropagation) {
        e.stopPropagation(); // stops the browser from redirecting.
      }
    
      // Get destination table id, row
      var dstTable = $(this.closest('table')).attr('id');
      var dstRow = $(this).closest('tr');
    
      // No need to process if src and dst table are the same
      if (srcTable !== dstTable) {
      
        // Get source transfer data
        var srcData = e.dataTransfer.getData('text/plain');
    
        // Add row to destination Datatable
        $('#' + dstTable).DataTable().row.add($(srcData)).draw();
    
        // Remove ro from source Datatable
        $('#' + srcTable).DataTable().row(dragSrcRow).remove().draw();
    
      }
      return false;
    }
    
    function handleDragEnd(e) {
      // this/e.target is the source node.
      
      // Reset the opacity of the source row
      this.style.opacity = '1.0';
    
      // Clear 'over' class from both tables
      // and reset opacity
      [].forEach.call(rows, function (row) {
        row.classList.remove('over');
        row.style.opacity = '1.0';
      });
    
      [].forEach.call(rows2, function (row) {
        row.classList.remove('over');
        row.style.opacity = '1.0';
      });
    }
    
    
    
    } );
      </script>
      
    </head>
    <body>
        <div class="container">
          <table id="example" class="tablegrid display nowrap" width="100%">
            <thead>
              <tr>
                <th>Name</th>
                <th>Position</th>
                <th>Office</th>
                <th>Age</th>
                <th>Start date</th>
                <th>Salary</th>
              </tr>
            </thead>
    
            <tfoot>
              <tr>
                <th>Name</th>
                <th>Position</th>
                <th>Office</th>
                <th>Age</th>
                <th>Start date</th>
                <th>Salary</th>
              </tr>
            </tfoot>
    
          </table>
    
          <table id="example2" class="tablegrid display nowrap" width="100%">
            <thead>
              <tr>
                <th>Name</th>
                <th>Position</th>
                <th>Office</th>
                <th>Age</th>
                <th>Start date</th>
                <th>Salary</th>
              </tr>
            </thead>
    
            <tfoot>
              <tr>
                <th>Name</th>
                <th>Position</th>
                <th>Office</th>
                <th>Age</th>
                <th>Start date</th>
                <th>Salary</th>
              </tr>
            </tfoot>
    
          </table>
       </div>
    </body>
    </html>
    
  • colincolin Posts: 15,240Questions: 1Answers: 2,599

    Hi @Sammy1 ,

    Would you be able to link your page, or create a test case perhaps? Information on how to create a test case (if you aren't able to link to the page you are working on) is available here.

    Cheers,

    Colin

  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin

    If a test case isn't possible (hopefully it is!) then could you use debugger to give me a trace please - click the Upload button and then let me know what the debug code is.

    Allan

  • Sammy1Sammy1 Posts: 13Questions: 4Answers: 0

    I used fiddler and I see that it returns this.

    c3
    "{ \"data\" :[[ \"Tiger Nixon\",\"System Architect\", \"Edinburgh\", \"5421\",\"2011/04/25\",\"$320,800\"],[\"Garrett Winters\",\"Accountant\",\"Tokyo\", \"8422\",\"2011/07/25\",\"$170,750\"]] }"
    0

  • Sammy1Sammy1 Posts: 13Questions: 4Answers: 0

    from my web api from .net core 2.1

  • colincolin Posts: 15,240Questions: 1Answers: 2,599

    Hi @Sammy1 ,

    It looks like it's being backslashed, which wouldn't be valid JSON. This thread should help, it's asking the same thing.

    Cheers,

    Colin

  • Sammy1Sammy1 Posts: 13Questions: 4Answers: 0

    Ok, I got it fixed. Hope it will help out the next person who runs into the same problem.

    This is for .net core web api
    1. In Visual Studio, create the web api project
    2. Add a class called DeChunkerMiddleware with the following content
    /* Credit: Pharylon for providing the middleware to get rid of the response returning chunked data. Found this in the forum: https://stackoverflow.com/questions/37966039/disable-chunking-in-asp-net-core
    */
    public class DeChunkerMiddleware
    {
    private readonly RequestDelegate _next;

        public DeChunkerMiddleware(RequestDelegate next)
        {
            _next = next;
        }
    
        public async Task InvokeAsync(HttpContext context)
        {
            var originalBodyStream = context.Response.Body;
            using (var responseBody = new MemoryStream())
            {
                context.Response.Body = responseBody;
                long length = 0;
                context.Response.OnStarting(() =>
                {
                    context.Response.Headers.ContentLength = length;
                    return Task.CompletedTask;
                });
                await _next(context);
                //if you want to read the body, uncomment these lines.
                //context.Response.Body.Seek(0, SeekOrigin.Begin);
                //var body = await new StreamReader(context.Response.Body).ReadToEndAsync();
                length = context.Response.Body.Length;
                context.Response.Body.Seek(0, SeekOrigin.Begin);
                await responseBody.CopyToAsync(originalBodyStream);
            }
        }
    }
    

    ...continue on next comment since this post only allows only certain length

  • Sammy1Sammy1 Posts: 13Questions: 4Answers: 0
    edited May 2019

    ...continue from previous comment

    3) In the startup.cs, add the app.UseMiddleware<DeChunkerMiddleware>(); above app.UseMvc()
    4) Add a class called, for example, Employee:

    public class Employee
        {
            public string name { get; set; }
            public string position { get; set; }
            public string office { get; set; }
            public string age { get; set; }
    
            public string startdate { get; set; }
            public string salary { get; set; }
    
        }
    

    5) In the Controllers\ValuesController.cs
    a. Inherit the class from Controller instead of ControllerBase (if you inherit from ControllerBase you will get Json not found error in the return statement when compiling.
    b. you can use the default Get() method:

    // GET api/values
            [HttpGet]
            //[Produces("application/json")]
            public JsonResult Get()
            {
               
                List<Employee> employees = new List<Employee>();
                Employee e = new Employee();
                e.name = "Tiger Nixon";
                e.position = "System Architect";
                e.office = "Edinburgh";
                e.age = "5421";
                e.startdate = "2011/04/25";
                e.salary = "$320,800";
                employees.Add(e);
                Employee e1 = new Employee();
                e1.name = "Garrett Winters";
                e1.position = "Accountant";
                e1.office = "Tokyo";
                e1.age = "8422";
                e1.startdate = "2011/07/25";
                e1.salary = "$170,750";
                employees.Add(e1);
    
                return Json(new { data = employees.ToList() });
    }
    

    6) In the html file:

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width">
      <title>JS Bin</title>
      
      <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/dt-1.10.18/datatables.min.css"/>
     <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
      <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
    <script type="text/javascript" src="https://cdn.datatables.net/v/dt/dt-1.10.18/datatables.min.js"></script>
       <script type='text/javascript'>
    $(document).ready( function () {
      var dragSrcRow = null;  // Keep track of the source row
      var srcTable = '';  // Global tracking of table being dragged for 'over' class setting
      var rows = [];   // Global rows for #example
      var rows2 = [];  // Global rows for #example2
      
      var data2 = [
          [
          "Rhona Davidson",
          "Integration Specialist",
          "Tokyo",
          "6200",
          "2010/10/14",
          "$327,900"
        ],
        [
          "Colleen Hurst",
          "Javascript Developer",
          "San Francisco",
          "2360",
          "2009/09/15",
          "$205,500"
        ],
        [
          "Sonya Frost",
          "Software Engineer",
          "Edinburgh",
          "1667",
          "2008/12/13",
          "$103,600"
        ],
        [
          "Jena Gaines",
          "Office Manager",
          "London",
          "3814",
          "2008/12/19",
          "$90,560"
        ],
    ]
        var url = 'https://ace4df59-a03e-4385-b3fa-f345d88e30b8.mock.pstmn.io/yard  ';
      $(document).ready(function() {
    var table = $('#example').DataTable({
      "ajax": url,
            "columns": [
                { "data": "name" },
                { "data": "position" },
                { "data": "office" },
                { "data": "age" },
                { "data": "start_date" },
                { "data": "salary" }
            ]
        ,
        paging: true,
        
        // Add HTML5 draggable class to each row
        createdRow: function ( row, data, dataIndex, cells ) {
          $(row).attr('draggable', 'true');
        },
        
        drawCallback: function () {
          // Add HTML5 draggable event listeners to each row
          rows = document.querySelectorAll('#example tbody tr');
            [].forEach.call(rows, function(row) {
              row.addEventListener('dragstart', handleDragStart, false);
              row.addEventListener('dragenter', handleDragEnter, false)
              row.addEventListener('dragover', handleDragOver, false);
              row.addEventListener('dragleave', handleDragLeave, false);
              row.addEventListener('drop', handleDrop, false);
              row.addEventListener('dragend', handleDragEnd, false);
            });
        }
      });
    } );
      
      
      
      var table2 = $('#example2').DataTable({
        data: data2,
        paging: false,
        
        // Add HTML5 draggable class to each row
        createdRow: function ( row, data, dataIndex, cells ) {
          $(row).attr('draggable', 'true');
        },
    
        drawCallback: function () {
          // Add HTML5 draggable event listeners to each row
          rows2 = document.querySelectorAll('#example2 tbody tr');
            [].forEach.call(rows2, function(row) {
              row.addEventListener('dragstart', handleDragStart, false);
              row.addEventListener('dragenter', handleDragEnter, false)
              row.addEventListener('dragover', handleDragOver, false);
              row.addEventListener('dragleave', handleDragLeave, false);
              row.addEventListener('drop', handleDrop, false);
              row.addEventListener('dragend', handleDragEnd, false);
            });
        }  
      });
      
    function handleDragStart(e) {
      // this / e.target is the source node.
      
      // Set the source row opacity
      this.style.opacity = '0.4';
      
      // Keep track globally of the source row and source table id
      dragSrcRow = this;
      srcTable = this.parentNode.parentNode.id
    
      // Allow moves
      e.dataTransfer.effectAllowed = 'move';
      
      // Save the source row html as text
      e.dataTransfer.setData('text/plain', e.target.outerHTML);
      
    }
      
    function handleDragOver(e) {
      if (e.preventDefault) {
        e.preventDefault(); // Necessary. Allows us to drop.
      }
    
      // Allow moves
      e.dataTransfer.dropEffect = 'move'; 
    
      return false;
    }
    
    function handleDragEnter(e) {
      // this / e.target is the current hover target.  
      
      // Get current table id
      var currentTable = this.parentNode.parentNode.id
      
      // Don't show drop zone if in source table
      if (currentTable !== srcTable) {
        this.classList.add('over');
      }
    }
    
    function handleDragLeave(e) {
      // this / e.target is previous target element.
      
      // Remove the drop zone when leaving element
      this.classList.remove('over');  
    }
      
    function handleDrop(e) {
      // this / e.target is current target element.
    
      if (e.stopPropagation) {
        e.stopPropagation(); // stops the browser from redirecting.
      }
    
      // Get destination table id, row
      var dstTable = $(this.closest('table')).attr('id');
      var dstRow = $(this).closest('tr');
    
      // No need to process if src and dst table are the same
      if (srcTable !== dstTable) {
      
        // Get source transfer data
        var srcData = e.dataTransfer.getData('text/plain');
    
        // Add row to destination Datatable
        $('#' + dstTable).DataTable().row.add($(srcData)).draw();
    
        // Remove ro from source Datatable
        $('#' + srcTable).DataTable().row(dragSrcRow).remove().draw();
    
      }
      return false;
    }
    
    function handleDragEnd(e) {
      // this/e.target is the source node.
      
      // Reset the opacity of the source row
      this.style.opacity = '1.0';
    
      // Clear 'over' class from both tables
      // and reset opacity
      [].forEach.call(rows, function (row) {
        row.classList.remove('over');
        row.style.opacity = '1.0';
      });
    
      [].forEach.call(rows2, function (row) {
        row.classList.remove('over');
        row.style.opacity = '1.0';
      });
    }
    
    
    
    } );
      </script>
      
    </head>
    <body>
        <div class="container">
          <table id="example" class="tablegrid display nowrap" width="100%">
            <thead>
              <tr>
                <th>Name</th>
                <th>Position</th>
                <th>Office</th>
                <th>Age</th>
                <th>Start date</th>
                <th>Salary</th>
              </tr>
            </thead>
    
            <tfoot>
              <tr>
                <th>Name</th>
                <th>Position</th>
                <th>Office</th>
                <th>Age</th>
                <th>Start date</th>
                <th>Salary</th>
              </tr>
            </tfoot>
    
          </table>
    
          <table id="example2" class="tablegrid display nowrap" width="100%">
            <thead>
              <tr>
                <th>Name</th>
                <th>Position</th>
                <th>Office</th>
                <th>Age</th>
                <th>Start date</th>
                <th>Salary</th>
              </tr>
            </thead>
    
            <tfoot>
              <tr>
                <th>Name</th>
                <th>Position</th>
                <th>Office</th>
                <th>Age</th>
                <th>Start date</th>
                <th>Salary</th>
              </tr>
            </tfoot>
    
          </table>
       </div>
    </body>
    </html>
    

    Edited by Allan - Syntax highlighting. Details on how to highlight code using markdown can be found in this guide.

  • Sammy1Sammy1 Posts: 13Questions: 4Answers: 0
    edited May 2019

    If you receive a CORS error you will need to add the following to the startup.cs in your .net core 2.1 (see the services.AddCors below, and

      public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddCors(options =>
                {
                    options.AddDefaultPolicy(
                        builder =>
                        {
    
                            builder.WithOrigins("file://");
                        });
    
    
                });
    

    and in the controllers get method (applied locally) add the following attribute above the Get method
    [EnableCors]

    Edited by Allan - Syntax highlighting. Details on how to highlight code using markdown can be found in this guide.

  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin

    Excellent - thanks for posting your findings and good to hear you got it working.

    Allan

This discussion has been closed.