ASP.NET Core React SPA中Customer接口POST请求404问题求助
我正在用React.js开发ASP.NET Core单页应用,调用Customer相关POST接口时遇到404 Not Found(无匹配路由)错误。已经用Postman测试过对应数据,还是没找到问题。我对这套技术经验不足,如有遗漏信息或需要查看更多代码,请告知,谢谢帮助!
CustomerController.cs
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using KCA.Core.Exceptions; using KCA.Core.Interfaces; using KCA.Core.Models; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace KCA.WebApi.Controllers { [Route("api/[controller]")] [ApiController] public class CustomerController : ControllerBase { private readonly ICustomerServices _customerServices; public CustomerController(ICustomerServices customerServices) { _customerServices = customerServices; } // GET: api/Customer [HttpGet] public async Task<ActionResult<IEnumerable<Customer>>> GetCustomers() { try { var customers = await _customerServices.GetAllAsync(); return Ok(customers); } catch (Exception ex) { return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); } } // GET: api/Customer/{id} [HttpGet("{id}")] public async Task<ActionResult<Customer>> GetCustomer(Guid id) { try { var customer = await _customerServices.GetByIdAsync(id); return Ok(customer); } catch (NotFoundException ex) { return NotFound(ex.Message); } catch (BadRequestException ex) { return BadRequest(ex.Message); } catch (Exception ex) { return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); } } // POST: api/Customer [HttpPost] public async Task<ActionResult<Customer>> PostCustomer([FromBody] Customer customer) { try { await _customerServices.AddAsync(customer); return CreatedAtAction(nameof(GetCustomer), new { id = customer.Id }, customer); } catch (BadRequestException ex) { return BadRequest(ex.Message); } catch (ConflictException<Customer> ex) { return Conflict(ex.Message); } catch (Exception ex) { return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); } } // PUT: api/Customer/{id} [HttpPut("{id}")] public async Task<IActionResult> PutCustomer(Guid id, [FromBody] Customer customer) { if (id != customer.Id) { return BadRequest("Customer ID mismatch."); } try { await _customerServices.UpdateAsync(customer); return NoContent(); } catch (NotFoundException ex) { return NotFound(ex.Message); } catch (BadRequestException ex) { return BadRequest(ex.Message); } catch (Exception ex) { return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); } } // DELETE: api/Customer/{id} [HttpDelete("{id}")] public async Task<IActionResult> DeleteCustomer(Guid id) { try { await _customerServices.DeleteAsync(id); return NoContent(); } catch (NotFoundException ex) { return NotFound(ex.Message); } catch (BadRequestException ex) { return BadRequest(ex.Message); } catch (Exception ex) { return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); } } } }
Program.cs(含Startup类)
using KCA.Infrastructure; using KCA.Core.Models; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.UI; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using KCA.Infrastructure.Data; using KCA.Core.Interfaces; using KCA.Core.Services; namespace KCA.Web { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } 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) { var connectionString = Configuration.GetConnectionString("DefaultConnection"); services.AddDbContext<KCAContext>(options => options.UseSqlServer(connectionString)); services.AddDatabaseDeveloperPageExceptionFilter(); services.AddDefaultIdentity<Employee>(options => options.SignIn.RequireConfirmedAccount = false) .AddEntityFrameworkStores<KCAContext>() .AddDefaultTokenProviders(); services.AddIdentityServer() .AddApiAuthorization<Employee, KCAContext>(); //Add Services services.AddScoped<IKCAContext, KCAContext>(); services.AddScoped<ICarService, CarServices>(); services.AddScoped<ICustomerServices, CustomerServices>(); services.AddScoped<IEmployeeServices, EmployeeServices>(); services.AddScoped<IPartsService, PartsService>(); services.AddScoped<IJobServices, JobServices>(); // Add CORS policy services.AddCors(options => { options.AddPolicy("AllowAll", builder => { builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); services.AddControllersWithViews(); services.AddRazorPages(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseMigrationsEndPoint(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseIdentityServer(); app.UseAuthorization(); app.UseCors("AllowAll"); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller}/{action=Index}/{id?}"); endpoints.MapRazorPages(); endpoints.MapFallbackToFile("index.html"); }); } } }
Postman请求信息
我在Postman中使用以下请求测试,仍返回404:
POST https://localhost:44401//api/Customer Body: (JSON) { "Id": "b63da63d-5fd5-4b5f-8e33-4d4e7c9db927", "FirstName": "John", "LastName": "Doe", "ContactNumber": "555-123-4567", "Email": "john.doe@example.com", "Address": "123 Main St", "Postcode": "12345" }
Customer模型
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace KCA.Core.Models { public class Customer { public Guid Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } [Phone] public string ContactNumber { get; set; } [EmailAddress] public string Email { get; set; } public string Address { get; set; } public string Postcode { get; set; } //Relational data public virtual ICollection<Car> Cars { get; set; } public virtual ICollection<Job> Jobs { get; set; } } }
排查与解决方案
修正请求URL的多余斜杠
你当前的请求URL是https://localhost:44401//api/Customer,域名与api之间多了一个斜杠,这会导致路由匹配失败。将URL改为https://localhost:44401/api/Customer后重试。确保API控制器路由被正确注册
在Startup.ConfigureServices中,当前仅调用了services.AddControllersWithViews(),建议添加services.AddControllers()明确注册API控制器服务,确保框架能扫描到CustomerController:services.AddControllers(); // 添加这一行 services.AddControllersWithViews(); services.AddRazorPages();调整路由匹配顺序
在Startup.Configure的UseEndpoints中,API控制器路由应优先于SPA fallback规则,避免API请求被误拦截。修改为:app.UseEndpoints(endpoints => { endpoints.MapControllers(); // 优先处理API路由 endpoints.MapControllerRoute( name: "default", pattern: "{controller}/{action=Index}/{id?}"); endpoints.MapRazorPages(); endpoints.MapFallbackToFile("index.html"); });验证控制器程序集扫描
如果CustomerController所在的KCA.WebApi是独立类库,需要在AddControllers中指定程序集,确保框架能发现该控制器:services.AddControllers() .AddApplicationPart(typeof(KCA.WebApi.Controllers.CustomerController).Assembly);检查端口是否正确
确认Postman中使用的端口44401与ASP.NET Core项目启动时的HTTPS端口一致,可以在项目属性的“调试”选项卡中查看实际端口。
内容的提问来源于stack exchange,提问作者Cameron Logan

