You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core API接口/api/plants/search持续返回404排查求助

ASP.NET Core API /api/plants/search 接口返回404问题排查

问题描述

我正在开发一个ASP.NET Core API,请求/api/plants/search接口时持续返回404错误,尽管控制器和路由配置看似无误。需要识别代码中可能导致该接口出现404错误的配置问题。

已完成的排查步骤

  • 本地开发环境部署时一切正常,推测控制器配置无误;
  • 确认API已正常运行且可访问数据库;
  • 检查了UseCors、UseAuthentication、UseAuthorization及端点路由的中间件顺序,确保配置正确;
  • 确认Program.cs中已调用app.MapControllers();;
  • 初始配置的app.MapFallbackToController("Index", "Fallback");已被注释,避免干扰API路由;
  • 检查IIS配置并禁用了默认文档。

相关代码文件

Program.cs

using API;
using API.Data;
using API.Extensions;
using API.Middleware;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// 向容器中添加服务。
builder.Services.AddControllers();
builder.Services.AddApplicationServices(builder.Configuration);
builder.Services.AddIdentityServices(builder.Configuration);
builder.Logging.AddConsole();

var connString = "";
connString = builder.Configuration.GetConnectionString("DefaultConnection");

builder.Services.AddDbContext<DataContext>(opt =>
{
    opt.UseSqlServer(connString);
});

var app = builder.Build();

// 配置HTTP请求管道。
app.UseMiddleware<ExceptionMiddleware>();

app.UseCors("AllowSpecificOrigin");
app.UseAuthentication();
app.UseAuthorization();

// app.UseDefaultFiles();
// app.UseStaticFiles();

app.MapControllers();
// app.MapFallbackToController("Index", "Fallback");

using var scope = app.Services.CreateScope();
var services = scope.ServiceProvider;
try 
{
    var context = services.GetRequiredService<DataContext>();
    await context.Database.MigrateAsync();
    await Seed.SeedAdmin(context);
}
catch (Exception ex)
{
    var logger = services.GetService<ILogger<Program>>();
    logger.LogError(ex, "迁移过程中发生错误");
}

app.Run();

appsettings.json(已移除敏感信息)

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "CONNECTION STRING"
  },
  "TokenKey": "TOKEN KEY"
}

ApplicationServiceExtensions.cs

using API.Data;
using API.Interfaces;
using API.Services;

namespace API.Extensions
{
    public static class ApplicationServiceExtensions
    {
        public static IServiceCollection AddApplicationServices(this IServiceCollection services, 
            IConfiguration config)
        {
            services.AddCors(options =>
            {
                options.AddPolicy("AllowSpecificOrigin",
                    builder => builder.WithOrigins(
                        "https://localhost:4200",
                        "https://plantguide.ch",
                        "https://www.plantguide.ch",
                        "https://edgarsplantguide.com",
                        "https://www.edgarsplantguide.com"
                    )
                    .AllowAnyHeader()
                    .AllowAnyMethod());   
            });
            services.AddScoped<ITokenService, TokenService>();
            services.AddScoped<IPlantRepository, PlantRepository>();
            services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());

            return services;

        }
    }
}

BaseApiController.cs

using Microsoft.AspNetCore.Mvc;

namespace API.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class BaseApiController: ControllerBase
    {

    }
}

PlantsController.cs

using API.Data;
using API.Entities;
using API.Interfaces;
using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace API.Controllers;

public class PlantsController: BaseApiController
{
    private readonly IPlantRepository _plantRepository;
    private readonly IMapper _mapper;

    // 植物控制器构造函数
    public PlantsController(IPlantRepository plantRepository, IMapper mapper)
    {
       _mapper = mapper;
       _plantRepository = plantRepository;
    }

    [HttpPost("search")] // /api/plants/search
    public async Task<ActionResult<IEnumerable<Plant>>> SearchPlants([FromBody] PlantSearchFormDto searchDto)
    {
        var results = await _plantRepository.GetPlantsBasedOnSearchFormContentAsync(searchDto);
        return Ok(results);
    }

    [HttpGet("{id}")] // /api/plants/1
    public async Task<ActionResult<Plant>> GetPlantByIdAsync(int id)
    {
        return await _plantRepository.GetPlantByIdAsync(id);
    }
}

可能的问题点及排查建议

  1. 请求方式不匹配:接口定义为[HttpPost("search")],如果实际请求使用GET方法,会直接返回404。务必确认请求使用POST发送。
  2. 模型绑定失败:如果请求体格式与PlantSearchFormDto不匹配,可能触发400错误,但部分场景下会被误判为404。可以临时将参数改为[FromBody] dynamic searchDto测试,或者提高日志级别(将Microsoft.AspNetCore设为Debug)查看模型绑定详情。
  3. 路由注册异常:部署环境中可能存在程序集加载问题,导致PlantsController未被正确注册。可在Program.cs的app.Run()前添加代码输出所有已注册路由,确认/api/plants/search是否存在:
    var endpointDataSource = app.Services.GetRequiredService<EndpointDataSource>();
    foreach (var endpoint in endpointDataSource.Endpoints)
    {
        if (endpoint is RouteEndpoint routeEndpoint)
        {
            app.Logger.LogInformation("已注册路由: {Route}", routeEndpoint.RoutePattern.RawText);
        }
    }
    
  4. CORS预检请求失败:若请求来源不在AllowSpecificOrigin列表中,OPTIONS预检请求会失败,前端可能表现为404(实际是CORS错误)。检查请求的Origin是否在允许列表内,或临时改为AllowAnyOrigin()测试。
  5. IIS路由干扰:即使禁用了默认文档,IIS的URL重写规则可能拦截请求。检查站点的URL重写模块配置,确保没有影响/api/plants/search的规则。

内容的提问来源于stack exchange,提问作者Philipp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 13:55:00