locked
Blazor WASM with CRUD against Web API Endpoint - not working RRS feed

  • Question

  • User379720387 posted

    Making some progress, now have it almost working up to where it is, not yet, showing some data. The api works and I get a json string returned in the browser.

    The students.razor functionality is not working right.

    Below is the original code as per this tutorial.

    The two highlighted methods have squiggles:

    @page "/students"
    @inject HttpClient httpClient
    
    <h1>Students</h1>
    
    <p>This component demonstrates fetching data from the server.</p>
    
    @if (students == null) {
    <p><em>Loading...</em></p>
    } else {
    <table class='table table-hover'>
      <thead>
        <tr>
          <th>ID</th>
          <th>First Name</th>
          <th>Last Name</th>
          <th>School</th>
        </tr>
      </thead>
      <tbody>
        @foreach (var item in students) {
        <tr>
          <td>@item.StudentId</td>
          <td>@item.FirstName</td>
          <td>@item.LastName</td>
          <td>@item.School</td>
        </tr>
        }
      </tbody>
    </table>
    }
    
    @functions {
      Student[] students;
      string baseUrl = "https://localhost:44318/";
    
      protected override async Task OnInitAsync() {
        await load();
      }
    
      protected async Task load() {
        students = await httpClient.GetJsonAsync<Student[]>($"{baseUrl}api/students");
      }
    }

    OnInitAsync() has been replaced by with OnInitializedAsync(), and apparentlly await.Load() has been replace by return base.On....

    protected override Task OnInitializedAsync()
            {
                return base.OnInitializedAsync();
            }

    GetJsonAsync<> also no longer exists and it has been replace with GetFromJsonAsync<>

    The new code looks like this:

    @functions {
        Student[] students;
        string baseUrl = "https://localhost:44366/";
    
            protected override Task OnInitializedAsync()
            {
                return base.OnInitializedAsync();
            }
            
            protected async Task load()
            {
                students = await httpClient.GetFromJsonAsync<Student[]>($"{baseUrl}api/students");
            }
    }

    It just shows loading....

    Console is clear

    Network tab says HTTP 200 for headers , and network tab preview says an unhandled error has occurred.

    Any suggestions on how to troubleshoot this further?

    Wednesday, May 20, 2020 6:50 PM

Answers

  • User475983607 posted

    Most likely the Web API Application is not running.  Right click the Web API project and set it as the startup project.  Then press ctrl-F5.  This hosts the Web API project in IIS Express.  Next right click on the Blazor app and set it as the start up project.  Run the Blazor project. 

    There are other ways to configure multiple projects to start.  Solution properties, command line, etc.  

    I prefer to use dotnet run to start service projects.  Usually I use a powershell script but you execute the command from the package manage console.  

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, May 27, 2020 6:04 PM
  • User475983607 posted

    I received the same error message with your original project code until I noticed the URL was wrong.   Fixing the problem was super easy.  I ran the Web API project and copied the URL from the browser's address bar to the Blazor httpClient.  Build -> run.

    I verified your latest code works.  My best guess is you did not restart or refresh the Blazor application after making changes.  You are seeing the previous error message. 

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, May 27, 2020 7:01 PM

All replies

  • User-821857111 posted

    Try

    @functions {
        Student[] students;
        string baseUrl = "https://localhost:44366/";
    
        protected override Task OnInitializedAsync()
        {
            students = await httpClient.GetFromJsonAsync<Student[]>($"{baseUrl}api/students");
    }
    }
    Tuesday, May 26, 2020 1:28 PM
  • User379720387 posted

    squiggles:

    Students.OnInitializedAsync(): not all code paths return a value

    The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to Task<Task>

    protected override async Task OnInitializedAsync() makes VS happy.

    Still not returning any data, however there are errors showing up in the console:

    dotnet.3.2.0.js:1 GET https://localhost:44366/api/students net::ERR_CONNECTION_REFUSED
    _mono_wasm_invoke_js_with_args @ dotnet.3.2.0.js:1
    do_icall @ dotnet.wasm:1
    do_icall_wrapper @ dotnet.wasm:1
    interp_exec_method @ dotnet.wasm:1
    interp_runtime_invoke @ dotnet.wasm:1
    mono_jit_runtime_invoke @ dotnet.wasm:1
    do_runtime_invoke @ dotnet.wasm:1
    mono_runtime_invoke_checked @ dotnet.wasm:1
    mono_runtime_try_invoke_array @ dotnet.wasm:1
    ves_icall_InternalInvoke @ dotnet.wasm:1
    ves_icall_InternalInvoke_raw @ dotnet.wasm:1
    do_icall @ dotnet.wasm:1
    do_icall_wrapper @ dotnet.wasm:1
    interp_exec_method @ dotnet.wasm:1
    interp_runtime_invoke @ dotnet.wasm:1
    mono_jit_runtime_invoke @ dotnet.wasm:1
    do_runtime_invoke @ dotnet.wasm:1
    mono_runtime_try_invoke @ dotnet.wasm:1
    mono_runtime_invoke @ dotnet.wasm:1
    mono_wasm_invoke_method @ dotnet.wasm:1
    Module._mono_wasm_invoke_method @ dotnet.3.2.0.js:1
    call_method @ dotnet.3.2.0.js:1
    (anonymous) @ dotnet.3.2.0.js:1
    beginInvokeDotNetFromJS @ blazor.webassembly.js:1
    s @ blazor.webassembly.js:1
    e.invokeMethodAsync @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    r @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    r @ blazor.webassembly.js:1
    d @ blazor.webassembly.js:1
    f @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    e.onGlobalEvent @ blazor.webassembly.js:1
    Show 12 more frames from Library code
    blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
    Unhandled exception rendering component: TypeError: Failed to fetch
    WebAssembly.JSException: TypeError: Failed to fetch
    at System.Net.Http.WebAssemblyHttpHandler.doFetch (System.Threading.Tasks.TaskCompletionSource`1[TResult] tcs, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x2e3d368 + 0x00a30> in <filename unknown>:0
    at System.Net.Http.WebAssemblyHttpHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x2e08688 + 0x00174> in <filename unknown>:0
    at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered (System.Threading.Tasks.Task`1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) <0x2e16780 + 0x00134> in <filename unknown>:0
    at System.Net.Http.Json.HttpClientJsonExtensions.GetFromJsonAsyncCore[T] (System.Threading.Tasks.Task`1[TResult] taskResponse, System.Text.Json.JsonSerializerOptions options, System.Threading.CancellationToken cancellationToken) <0x2e22e10 + 0x000cc> in <filename unknown>:0
    at SchoolClient.Pages.Students.OnInitializedAsync () [0x00043] in C:\Users\Robert\source\Repos\SchoolNew\SchoolClient\Pages\Students.razor:43
    at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync () <0x2b6d960 + 0x0013a> in <filename unknown>:0
    at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask (System.Threading.Tasks.Task taskToHandle) <0x2db8a78 + 0x000b6> in <filename unknown>:0

    Tuesday, May 26, 2020 2:00 PM
  • User-821857111 posted

    The 'await' operator can only be used within an async method.
    Oops. That's correct.

    protected async override Task OnInitializedAsync()
    {
        students = await httpClient.GetFromJsonAsync<Student[]>($"{baseUrl}api/students");
    }
    Wednesday, May 27, 2020 7:54 AM
  • User379720387 posted

    Same error message about failed to fetch in browser dev tools..... I am using Edge.

    blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
          Unhandled exception rendering component: TypeError: Failed to fetch
    WebAssembly.JSException: TypeError: Failed to fetch
      at System.Net.Http.WebAssemblyHttpHandler.doFetch (System.Threading.Tasks.TaskCompletionSource`1[TResult] tcs, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x2cf96f0 + 0x00a30> in <filename unknown>:0 
      at System.Net.Http.WebAssemblyHttpHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x2ccca20 + 0x00174> in <filename unknown>:0 
      at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered (System.Threading.Tasks.Task`1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) <0x2ce2898 + 0x00134> in <filename unknown>:0 
      at System.Net.Http.Json.HttpClientJsonExtensions.GetFromJsonAsyncCore[T] (System.Threading.Tasks.Task`1[TResult] taskResponse, System.Text.Json.JsonSerializerOptions options, System.Threading.CancellationToken cancellationToken) <0x2cdc8b8 + 0x000cc> in <filename unknown>:0 
      at SchoolClient.Pages.Students.OnInitializedAsync () [0x00043] in C:\Users\Robert\source\Repos\SchoolNew\SchoolClient\Pages\Students.razor:43 
      at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync () <0x2b6d960 + 0x0013a> in <filename unknown>:0 
      at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask (System.Threading.Tasks.Task taskToHandle) <0x2cddac8 + 0x000b6> in <filename unknown>:0 
    f.printErr @ blazor.webassembly.js:1

    Wednesday, May 27, 2020 10:07 AM
  • User379720387 posted

    Made some progress.

    In another thread here someone removed the Url, that made the first error go away

    students = await httpClient.GetFromJsonAsync<Student[]>("api/students");

    The only error left in the console is now:

    crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
          Unhandled exception rendering component: The provided ContentType is not supported; the supported types are 'application/json' and the structured syntax suffix 'application/+json'.

    Then I found this:

    Apparently Web API now (3.2.0) returns text and not json. And that is what mine is doing too!

    Where and how can I tell Web API to return JSON?

    Wednesday, May 27, 2020 12:10 PM
  • User-821857111 posted

    Apparently Web API now (3.2.0) returns text and not json.
    That's not how I read the issue. Changing the default return type from Web API endpoints would be a major breaking change and would probably cause riots. That would be like making Web Pages return PDF files. It looks like the person who posted that issue either ran into a bug or configured their API incorrectly. It's difficult to tell what happened because the issue was closed without any resolution being posted.

    AFIAK, the default content type for Web API is still application/json.

    Anyhooo, use the Consumes attribute to force hte issue: https://docs.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-3.1#define-supported-request-content-types-with-the-consumes-attribute

    [Consumes("application/json")]
    public async Task<IActionResult> Blah()
    {

    }

    Wednesday, May 27, 2020 1:38 PM
  • User379720387 posted

    My controller looks slightly different:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.EntityFrameworkCore;
    using SchoolAPI;
    using SchoolLibrary;
    
    namespace SchoolAPI.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class StudentsController : ControllerBase
        {
            private readonly SchoolDbContext _context;
    
            public StudentsController(SchoolDbContext context)
            {
                _context = context;
            }
    
            // GET: api/Students
            [HttpGet]
            [Produces("application/json")]
            public async Task<ActionResult<IEnumerable<Student>>> GetStudents()
            {
                return await _context.Students.ToArrayAsync();
            }

    Tried both Produces and Consumes, neither of which helped, still sending text.

    Wednesday, May 27, 2020 3:51 PM
  • User475983607 posted

    I'm unable to reproduce this issue.  I create a new Blazor Web Assembly App.  Then I create a new Web API core application and configure CORS for the Blazor app.  I replaced the call to the JSON file (sample-data/weather.json) with the Web API URL.

        protected override async Task OnInitializedAsync()
        {
            forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("https://localhost:44379/weatherforecast");
        }

    It just worked. 

    Wednesday, May 27, 2020 4:27 PM
  • User379720387 posted

    My solution has the weather thingy and it works too!

    Wednesday, May 27, 2020 4:29 PM
  • User475983607 posted

    wavemaster

    My solution has the weather thingy and it works too!

    I'm not sure if you understand my post.  I created a separate Web API application made an HTTP request to the Web API application from the Blazor application.  It just worked using the standard HTTP client.

    Wednesday, May 27, 2020 4:47 PM
  • User475983607 posted

    Regarding your PM, the URL is incorrect.

        protected async override Task OnInitializedAsync()
        {
            students = await httpClient.GetFromJsonAsync<Student[]>("https://localhost:44366/api/students");
        }

    Or 

        protected async override Task OnInitializedAsync()
        {
            httpClient.BaseAddress = new Uri(@"https://localhost:44366/");
            students = await httpClient.GetFromJsonAsync<Student[]>("api/students");
        }

    The HTML/Text response is an error message.

    Wednesday, May 27, 2020 5:30 PM
  • User379720387 posted

    I had previously removed the URL, based on some comments at github.

    Now that it is back in there, as per your suggestion, the error message ERR_CONNECTION_REFUSED is back. The second error is "failed to fetch"

    Without the URL, there is a single error message about the content type.

    Wednesday, May 27, 2020 5:50 PM
  • User475983607 posted

    Most likely the Web API Application is not running.  Right click the Web API project and set it as the startup project.  Then press ctrl-F5.  This hosts the Web API project in IIS Express.  Next right click on the Blazor app and set it as the start up project.  Run the Blazor project. 

    There are other ways to configure multiple projects to start.  Solution properties, command line, etc.  

    I prefer to use dotnet run to start service projects.  Usually I use a powershell script but you execute the command from the package manage console.  

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, May 27, 2020 6:04 PM
  • User379720387 posted

    It looks like you are right about the Web API not running.

    Most likely the Web API Application is not running.  Right click the Web API project and set it as the startup project.  Then press ctrl-F5.  This hosts the Web API project in IIS Express.  Next right click on the Blazor app and set it as the start up project.  Run the Blazor project. 

    After this the error message is gone, but it has been replaced by a warning (I guess):

    The provided ContentType is not supported; the supported types are application/json and the structured syntax suffix application/+json

    With or without the url this warning is the same:

    This is what I have:

    [Route("api/[controller]")]
        [ApiController]
        public class StudentsController : ControllerBase
        {
            private readonly SchoolDbContext _context;
    
            public StudentsController(SchoolDbContext context)
            {
                _context = context;
            }
    
            // GET: api/Students
            [HttpGet]
            [Consumes("application/json")]
            public async Task<ActionResult<IEnumerable<Student>>> GetStudents()
            {
                return await _context.Students.ToArrayAsync();
            }

    in the client

    @functions {
        Student[] students;
        string baseUrl = "https://localhost:44366/";
    
        protected async override Task OnInitializedAsync()
        {
            httpClient.BaseAddress = new Uri(@"https://localhost:44366/");
            try {
                students = await httpClient.GetFromJsonAsync<Student[]>("api/students");
            }
            catch (Exception ex) { Console.WriteLine(ex.Message); }
        }

    Thoughts?

    Wednesday, May 27, 2020 6:26 PM
  • User475983607 posted

    I received the same error message with your original project code until I noticed the URL was wrong.   Fixing the problem was super easy.  I ran the Web API project and copied the URL from the browser's address bar to the Blazor httpClient.  Build -> run.

    I verified your latest code works.  My best guess is you did not restart or refresh the Blazor application after making changes.  You are seeing the previous error message. 

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, May 27, 2020 7:01 PM
  • User379720387 posted

    For some unknown reason I now get HTTP error 415 on the Web API project.

    Sorry, would really like to see this work, but I cannot right now.

    Wednesday, May 27, 2020 7:36 PM
  • User475983607 posted

    wavemaster

    For some unknown reason I now get HTTP error 415 on the Web API project.

    Sorry, would really like to see this work, but I cannot right now.

     415 is an unsupported media type.  This error is commonly due to a mistake with input parameter formatting or a missing "Accept" header. 

    Update the program.cs file to inject an HttpClientFactory that has the configuration you're looking for.   You'll need to add the Microsoft.Extensions.Http NuGet package to get the extensions installed.

            public static async Task Main(string[] args)
            {
                var builder = WebAssemblyHostBuilder.CreateDefault(args);
                builder.RootComponents.Add<App>("app");
    
                builder.Services.AddHttpClient("ServerAPI", client =>
                {
                    client.BaseAddress = new Uri(@"https://localhost:44366/");
                    client.DefaultRequestHeaders.Add("Accept", "application/json");
                });
    
                await builder.Build().RunAsync();
            }

    You'll also need to update the Blazor page.  This is a modified version of your code.  The code functions as expected.

    @page "/students"
    @inject IHttpClientFactory ClientFactory
    
    <h1>Students</h1>
    
    <p>This component demonstrates fetching data from the server.</p>
    
    @if (students == null)
    {
        <p><em>Loading...</em></p>
    }
    else
    {
        <table class='table table-hover'>
            <thead>
                <tr>
                    <th>ID</th>
                    <th>First Name</th>
                    <th>Last Name</th>
                    <th>School</th>
                </tr>
            </thead>
            <tbody>
                @foreach (var item in students)
                {
                    <tr>
                        <td>@item.StudentId</td>
                        <td>@item.FirstName</td>
                        <td>@item.LastName</td>
                        <td>@item.School</td>
                    </tr>
                }
            </tbody>
        </table>
    }
    
    @functions {
        Student[] students;
    
        protected async override Task OnInitializedAsync()
        {
            try
            {
                var client = ClientFactory.CreateClient("ServerAPI");
                students = await client.GetFromJsonAsync<Student[]>("api/students");
            }
            catch (Exception ex) { Console.WriteLine(ex.Message); }
        }
    }

    Reference

    https://docs.microsoft.com/en-us/aspnet/core/blazor/call-web-api?view=aspnetcore-3.1

    Wednesday, May 27, 2020 8:05 PM
  • User379720387 posted

    Thanks all, got it working, finally.

    I am interested in learning more about starting multiple project as was indicated by mgebhard:

    Will create a separate post for that.

    Thursday, May 28, 2020 2:04 PM