Blazor web assembly, calling an ASP.NET Core Web API : GET works, but POST does nothing when called from the Blazor client - Sta

admin2025-05-01  0

I am creating an ASP.NET Core Web API that will do CRUD for a Locations entity. I call the API from my Blazor web assembly project, and the GET verb I have in place works (I can list locations from my database in a .razor component page, in a grid), but when I try to do a POST from the same client to the same route, nothing happens. I can 'try it out' in swagger and my POST code works (it inserts the new location), but from the web assembly client, there is no result and the new location is not inserted.

What might I be missing?

Here's my API LocationController:

using Microsoft.AspNetCore.Mvc;
using BlazorAppDiscoveryAPI.Models;
using Microsoft.Data.SqlClient;
using Dapper;
using System.Data;

namespace BlazorAppDiscoveryAPI.Controllers
{
    [ApiController]
    [Route("api/Locations")]
    public class LocationController : ControllerBase
    {
        static IConfiguration conf = (new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json").Build());
        public static string connectionString = conf["ConnectionStrings:Default"].ToString();

        // This works, I can display the Locations from the db on my blazor client page.
        [HttpGet(Name = "GetLocations")]
        public IEnumerable<dynamic> Get()
        {
            string strSQL = "select * from dbo.Location";

            using IDbConnection connection = new SqlConnection(connectionString);
            {
                return connection.Query(strSQL);
            }
        }

        // This works in swagger, but not when called from the blazor client page. I'm not sure the client call even gets to here.
        [HttpPost]
        public int Add(LocationModel newLocation)
        {
            string strSQL = @"INSERT INTO dbo.Location(Location_NUM, Location_OWNER)
                            VALUES(@Location_NUM, @Location_OWNER);";

            using IDbConnection connection = new SqlConnection(connectionString);
            {
                return connection.Execute(strSQL, newLocation);               
            }
        }
    }
}

Again, this much works in swagger for a GET and POST to localhost.../api/Locations/.

From the client side: I'm calling the API from the Blazor component page:

@code {
    private List<LocationModel> Locations;
    private LocationModel newLocation = new LocationModel();

    protected override async Task OnInitializedAsync()
    {
        Locations = await LocationService.GetAllLocationsAsync();
    }

    private async Task InsertLocation()
    {
        LocationModel s = new LocationModel
            {
                Location_NUM = newLocation.Location_NUM,
                Location_OWNER = newLocation.Location_OWNER
            };

        await LocationService.InsertLocationAsync(s);

        newLocation = new LocationModel();
    }
}

Here is the LocationService.cs, making the requests:

using BlazorAppDiscovery.Client.Models;
using System.Net.Http.Json;
using Microsoft.Extensions.Configuration;

namespace BlazorAppDiscovery.Client.Services
{
    public class LocationService(HttpClient http) : ILocationService
    {
        public static string baseaddress = "https://localhost:7276/"; // TODO: get this from the config file.

        public async Task<List<LocationModel>> GetAllLocationsAsync()
        { 
            string requestUri = baseaddress + "api/Locations/";
            return await http.GetFromJsonAsync<List<LocationModel>>(requestUri);
        }

        public async Task InsertLocationAsync(LocationModel newLocation)
        {
            string requestUri = baseaddress + "api/Locations/";
            await http.PostAsJsonAsync(requestUri, newLocation);
        }
    }
}

What else might I have missed so that my GET works perfectly from both the client and swagger, but the POST only works from swagger, not the client?

I am creating an ASP.NET Core Web API that will do CRUD for a Locations entity. I call the API from my Blazor web assembly project, and the GET verb I have in place works (I can list locations from my database in a .razor component page, in a grid), but when I try to do a POST from the same client to the same route, nothing happens. I can 'try it out' in swagger and my POST code works (it inserts the new location), but from the web assembly client, there is no result and the new location is not inserted.

What might I be missing?

Here's my API LocationController:

using Microsoft.AspNetCore.Mvc;
using BlazorAppDiscoveryAPI.Models;
using Microsoft.Data.SqlClient;
using Dapper;
using System.Data;

namespace BlazorAppDiscoveryAPI.Controllers
{
    [ApiController]
    [Route("api/Locations")]
    public class LocationController : ControllerBase
    {
        static IConfiguration conf = (new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json").Build());
        public static string connectionString = conf["ConnectionStrings:Default"].ToString();

        // This works, I can display the Locations from the db on my blazor client page.
        [HttpGet(Name = "GetLocations")]
        public IEnumerable<dynamic> Get()
        {
            string strSQL = "select * from dbo.Location";

            using IDbConnection connection = new SqlConnection(connectionString);
            {
                return connection.Query(strSQL);
            }
        }

        // This works in swagger, but not when called from the blazor client page. I'm not sure the client call even gets to here.
        [HttpPost]
        public int Add(LocationModel newLocation)
        {
            string strSQL = @"INSERT INTO dbo.Location(Location_NUM, Location_OWNER)
                            VALUES(@Location_NUM, @Location_OWNER);";

            using IDbConnection connection = new SqlConnection(connectionString);
            {
                return connection.Execute(strSQL, newLocation);               
            }
        }
    }
}

Again, this much works in swagger for a GET and POST to localhost.../api/Locations/.

From the client side: I'm calling the API from the Blazor component page:

@code {
    private List<LocationModel> Locations;
    private LocationModel newLocation = new LocationModel();

    protected override async Task OnInitializedAsync()
    {
        Locations = await LocationService.GetAllLocationsAsync();
    }

    private async Task InsertLocation()
    {
        LocationModel s = new LocationModel
            {
                Location_NUM = newLocation.Location_NUM,
                Location_OWNER = newLocation.Location_OWNER
            };

        await LocationService.InsertLocationAsync(s);

        newLocation = new LocationModel();
    }
}

Here is the LocationService.cs, making the requests:

using BlazorAppDiscovery.Client.Models;
using System.Net.Http.Json;
using Microsoft.Extensions.Configuration;

namespace BlazorAppDiscovery.Client.Services
{
    public class LocationService(HttpClient http) : ILocationService
    {
        public static string baseaddress = "https://localhost:7276/"; // TODO: get this from the config file.

        public async Task<List<LocationModel>> GetAllLocationsAsync()
        { 
            string requestUri = baseaddress + "api/Locations/";
            return await http.GetFromJsonAsync<List<LocationModel>>(requestUri);
        }

        public async Task InsertLocationAsync(LocationModel newLocation)
        {
            string requestUri = baseaddress + "api/Locations/";
            await http.PostAsJsonAsync(requestUri, newLocation);
        }
    }
}

What else might I have missed so that my GET works perfectly from both the client and swagger, but the POST only works from swagger, not the client?

Share Improve this question edited Jan 2 at 20:41 John Marquez asked Jan 2 at 20:09 John MarquezJohn Marquez 811 silver badge5 bronze badges 4
  • Does the call from InsertLocationAsync hit the Controller code? await http.PostAsJsonAsync(requestUri, newLocation) returns a HttpResponseMessage. What does it tell you? – MrC aka Shaun Curtis Commented Jan 2 at 22:26
  • I prefixed that call: HttpResponseMessage r = await http.PostAsJsonAsync(requestUri, newLocation) and its contents are the very helpful '400 Bad Request'. {StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers: { Date: Fri, 03 Jan 2025 01:04:33 GMT Server: Kestrel Transfer-Encoding: chunked Content-Type: application/problem+json; charset=utf-8 }} – John Marquez Commented Jan 3 at 1:07
  • You don't need string baseaddress + "api/Locations/"; Change it to just "api/Locations/"; because, according to the docs "When sending a HttpRequestMessage with a relative Uri, the message Uri will be added to the BaseAddress property to create an absolute Uri. Note that all characters after the right-most "/" in the base URI are excluded when combined with the message URI". If that doesn't solve the problem, I hope that helps a bit. – benjamin Commented Jan 3 at 5:36
  • 400 error is related to input parameter normally. Maybe you can check value of newLocation and the request content including url of variable requestUri. – Tiny Wang Commented Jan 3 at 7:36
Add a comment  | 

3 Answers 3

Reset to default 0

We have a document for blazor application to send http request. Then I noticed that you have namespace BlazorAppDiscovery.Client.Services which looks like you are working on a blazor web app interactive auto mode.

So that I created a blazor project like that, I added builder.Services.AddHttpClient(); in the Program.cs of the blazorapp instead of the blazorapp.Client. And in Counter.razor component in blazorapp.Client project, I have codes below and it worked well.

@rendermode InteractiveAuto
@inject HttpClient HttpClient

@code {
    private int currentCount = 0;

    private async Task IncrementCount()
    {
        currentCount++;
        var model = new LocationModel
        {
            Location_NUM = 1,
            Location_OWNER = "test"
        };
        var responseMesg = await HttpClient.PostAsJsonAsync("https://localhost:7245/api/Test", model);
        var res = await responseMesg.Content.ReadAsStringAsync();
    }
    
    public class LocationModel
    {
        public int Location_NUM { get; set; }
        public string Location_OWNER { get; set; }
    }
}

And this is my API.

[HttpPost]
public int Add(LocationModel newLocation)
{
    string strSQL = @"INSERT INTO dbo.Location(Location_NUM, Location_OWNER)
                    VALUES(@Location_NUM, @Location_OWNER);";

    //using IDbConnection connection = new SqlConnection(connectionString);
    //{
    //    return connection.Execute(strSQL, newLocation);
    //}
    return 1;
}

Based on the return error:

 '400 Bad Request'. {StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers: {   Date: Fri, 03 Jan 2025 01:04:33 GMT   Server: Kestrel   Transfer-Encoding: chunked   Content-Type: application/problem+json; charset=utf-8 }}

To quote [from developer.mozilla.org]:

The HTTP 400 Bad Request client error response status code indicates that the server would not process the request due to something the server considered to be a client error. The reason for a 400 response is typically due to malformed request syntax, invalid request message framing, or deceptive request routing.

On the assumption the endpoint is correct, the likely reason for this is an invalid submitted Json object. You need to check the validity of your submitted LocationModel. Are you handling nulls correctly?

The Bad Request error means something is wrong with the request syntax. It probably is a missing [FromBody]:

   [HttpPost]
 //public int Add(LocationModel newLocation)
   public int Add([FromBody] LocationModel newLocation)
   {
      ...
   }

If this doesn't help then post the exact Model class, it could be a nullability problem.

转载请注明原文地址:http://anycun.com/QandA/1746098642a91647.html