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

指定控制器不存在时,如何路由至BaseAppController?

实现不存在控制器时回退到BaseAppController的解决方案

这是个挺实用的需求,刚好我之前在ASP.NET Core项目里处理过类似场景。要实现当请求指向不存在的控制器时自动用BaseAppController处理,我们有两种靠谱的实现方式,下面给你一步步拆解:

方法一:自定义控制器选择器(推荐,更贴合框架机制)

ASP.NET Core默认的控制器选择器在找不到对应控制器时会抛出异常并返回404,我们可以通过继承DefaultControllerSelector并重写其逻辑,让它在找不到控制器时回退到BaseAppController。

步骤1:创建自定义控制器选择器

using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Logging;
using System.Linq;

public class FallbackControllerSelector : DefaultControllerSelector
{
    private readonly ILogger<FallbackControllerSelector> _logger;
    private ControllerModel _fallbackController;

    public FallbackControllerSelector(
        IControllerTypeProvider controllerTypeProvider,
        ApplicationPartManager applicationPartManager,
        ILogger<DefaultControllerSelector> logger)
        : base(controllerTypeProvider, applicationPartManager, logger)
    {
        _logger = logger;
    }

    public override ControllerModel SelectController(RouteContext context)
    {
        try
        {
            // 先尝试用默认逻辑查找控制器
            return base.SelectController(context);
        }
        catch (InvalidOperationException ex)
        {
            // 捕获"找不到控制器"的异常(默认逻辑会抛出此异常)
            if (ex.Message.Contains("No controller found"))
            {
                var requestedController = context.RouteData.Values["controller"].ToString();
                _logger.LogInformation($"控制器 '{requestedController}' 不存在,将回退到BaseAppController处理");

                // 懒加载获取BaseAppController的模型
                if (_fallbackController == null)
                {
                    var allControllerTypes = ControllerTypeProvider.GetControllerTypes();
                    var fallbackType = allControllerTypes.FirstOrDefault(t => t.Name == nameof(BaseAppController));
                    
                    if (fallbackType == null)
                    {
                        throw new InvalidOperationException("应用中未找到BaseAppController,请确认该控制器已正确定义");
                    }

                    _fallbackController = new ControllerModel(fallbackType, System.Array.Empty<object>())
                    {
                        ControllerName = fallbackType.Name.Replace("Controller", string.Empty)
                    };
                }

                // 更新路由数据中的控制器名称,确保后续动作选择逻辑正常工作
                context.RouteData.Values["controller"] = _fallbackController.ControllerName;
                return _fallbackController;
            }

            // 非找不到控制器的异常,重新抛出
            throw;
        }
    }
}

步骤2:注册自定义选择器

在Program.cs(.NET 6+)中,替换默认的控制器选择器服务:

var builder = WebApplication.CreateBuilder(args);

// 添加控制器服务,并替换默认的控制器选择器
builder.Services.AddControllers()
    .AddControllersAsServices() // 必须添加此配置,确保控制器以服务方式注册
    .ReplaceService<IControllerSelector, FallbackControllerSelector>();

var app = builder.Build();

app.UseRouting();
app.UseAuthorization();

app.MapControllers();

app.Run();

步骤3:调整BaseAppController的路由

为了让BaseAppController能匹配任意控制器名称的请求,需要修改它的路由属性:

// 同时保留原有路由(供子类继承)和通用路由(处理不存在的控制器请求)
[Route("api/[controller]")]
[Route("api/{controller}")]
public class BaseAppController : Controller
{
    // 示例动作方法
    [HttpGet("MyAction")]
    public IActionResult MyAction()
    {
        return Ok($"请求由BaseAppController处理,原请求控制器名称:{RouteData.Values["controller"]}");
    }
}

方法二:中间件修改路由数据(更简洁)

另一种思路是在请求管道中提前检查控制器是否存在,如果不存在就修改路由数据中的控制器名称为BaseApp,让默认的控制器选择器能找到BaseAppController。

步骤1:创建回退控制器中间件

using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using System.Linq;

public class FallbackControllerMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IControllerTypeProvider _controllerTypeProvider;

    public FallbackControllerMiddleware(RequestDelegate next, IControllerTypeProvider controllerTypeProvider)
    {
        _next = next;
        _controllerTypeProvider = controllerTypeProvider;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        if (context.GetRouteData().Values.TryGetValue("controller", out var controllerObj) &&
            controllerObj is string requestedController)
        {
            // 检查是否存在对应的控制器(注意控制器名称需要加上Controller后缀)
            bool controllerExists = _controllerTypeProvider.GetControllerTypes()
                .Any(t => t.Name.Equals($"{requestedController}Controller", StringComparison.OrdinalIgnoreCase));

            if (!controllerExists)
            {
                // 修改路由数据中的控制器名称为BaseApp
                context.GetRouteData().Values["controller"] = "BaseApp";
            }
        }

        await _next(context);
    }
}

// 扩展方法,方便注册中间件
public static class FallbackControllerMiddlewareExtensions
{
    public static IApplicationBuilder UseFallbackController(this IApplicationBuilder app)
    {
        return app.UseMiddleware<FallbackControllerMiddleware>();
    }
}

步骤2:注册中间件

在Program.cs中,将中间件添加到路由之后、端点之前:

var app = builder.Build();

app.UseRouting();

// 添加回退控制器中间件
app.UseFallbackController();

app.UseAuthorization();

app.MapControllers();

app.Run();

步骤3:调整BaseAppController的路由

和方法一一样,需要给BaseAppController添加通用路由:

[Route("api/[controller]")]
[Route("api/{controller}")]
public class BaseAppController : Controller
{
    // ... 你的动作方法
}

注意事项

  1. 动作方法存在性:回退到BaseAppController后,请求的动作方法必须存在于该控制器中,否则仍会返回404,这符合你的需求逻辑。
  2. 路由优先级:子类控制器的路由(如api/Some)会比BaseAppController的通用路由(api/{controller})更具体,所以正常请求仍会优先匹配子类控制器,不会受影响。
  3. 控制器命名规范:确保所有控制器都遵循ASP.NET Core的命名规范(以Controller结尾),否则自定义选择器或中间件的检查逻辑需要调整。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:31:30