Действие не сопоставляется с пользовательским маршрутизатором в ASP.NET Core 8 Web API

Вопрос или проблема

В моём проекте ASP.NET Core 8 Web API, написанном на C#, нам необходимо маршрутизировать следующие конечные точки к одному методу действия с пользовательским маршрутом в API.

Вот мой код – CustomRouter:

public class CustomRouter : IRouter
{
    private readonly IRouter _defaultRouter;

    public CustomRouter(IRouter defaultRouter)
    {
        _defaultRouter = defaultRouter;
    }

    public async Task RouteAsync(RouteContext context)
    {
        // Извлечение пути и строки запроса из запроса
        var path = context.HttpContext.Request.Path.Value;
        var queryString = context.HttpContext.Request.QueryString;

        // Разделение пути на сегменты для извлечения имени контроллера и имени сущности
        var pathSegments = path.Split("https://stackoverflow.com/");
        var controllerName = pathSegments.Length > 1 ? pathSegments[pathSegments.Length - 2] : string.Empty;
        var entityName = pathSegments.Length > 0 ? pathSegments[pathSegments.Length - 1] : string.Empty;
        var queryParameters = queryString.HasValue ? queryString.Value : string.Empty;

        // Проверка, является ли запрос GET-запросом и нацелен ли он на InsightsController
        if (controllerName.Equals("insights", StringComparison.OrdinalIgnoreCase) &&
            context.HttpContext.Request.Method.Equals("GET", StringComparison.OrdinalIgnoreCase))
        {
            // Установка RouteData для контроллера и действия
            context.RouteData.Values["controller"] = "Insights";
            context.RouteData.Values["action"] = "GetTest"; // Метод действия для вызова
            context.RouteData.Values["entity"] = entityName; // Передать имя сущности
            context.RouteData.Values["queryParameters"] = queryParameters; // Передать параметры запроса, если это необходимо

            context.HttpContext.Request.Path = new PathString("/api/v1/Insights/");
            context.HttpContext.Request.QueryString = new QueryString();
            // Передать запрос маршрутизатору по умолчанию или следующему промежуточному ПО, который обработает вызов контроллера
            await _defaultRouter.RouteAsync(context);
            return;
        }

        // Если совпадений не найдено, передать управление следующему промежуточному ПО
        await _defaultRouter.RouteAsync(context);
    }

    public VirtualPathData GetVirtualPath(VirtualPathContext context)
    {
        // Генерация виртуального пути, если это необходимо
        return _defaultRouter.GetVirtualPath(context);
    }
}

Добавление использования пользовательского маршрутизатора в классе Startup:

// Использовать пользовательский маршрутизатор
app.UseRouter(builder =>
  {
     // Создание маршрутизатора по умолчанию для передачи в CustomRouter
     var defaultRouter = builder.Build();

     // Регистрация CustomRouter
     builder.Routes.Add(new CustomRouter(defaultRouter));                  
  });

Класс контроллера:

[Route("api/v1/[controller]")]
[ApiController]
public class InsightsController : Controller
{
     [HttpGet]
     public async Task<string> Get(string entity, string id = default)
     {
     }
}

Пользовательский маршрутизатор был добавлен. Несмотря на наличие пользовательского маршрутизатора, метод Get контроллера Insights сопоставляется и выполняется для этого запроса http://localhost:63896/api/v1/Insights, но не для следующего запроса http://localhost:63896/api/v1/Insights/pdrsearch?rs:q=bug.

Гуглил, чтобы найти помощь/пример кода, также пробовал добавить customMiddleware. Пока удачи нет.

Ответ или решение

Для решения проблемы маршрутизации запросов в ASP.NET Core 8 Web API, когда мы пытаемся перенаправить несколько конечных точек на один метод действия с использованием пользовательского маршрутизатора, необходимо тщательно рассмотреть, как вы обрабатываете сегменты пути и параметры запроса.

Давайте начнем с вашего кода и определим несколько ключевых моментов, которые могут требовать корректировки.

Исправления в классе CustomRouter

Ваш текущий подход к извлечению имени контроллера и сущности из пути может быть не оптимален. Вместо разбивания строки на сегменты, рассмотрите возможность использования PathTokens для построения логики маршрутизации более надежным и понятным способом.

Вот переработанный вариант вашего CustomRouter:

public class CustomRouter : IRouter
{
    private readonly IRouter _defaultRouter;

    public CustomRouter(IRouter defaultRouter)
    {
        _defaultRouter = defaultRouter;
    }

    public async Task RouteAsync(RouteContext context)
    {
        var path = context.HttpContext.Request.Path.Value;
        var queryString = context.HttpContext.Request.QueryString;

        // Проверяем, начинается ли путь с ожидаемого сегмента
        if (path.StartsWith("/api/v1/Insights/", StringComparison.OrdinalIgnoreCase))
        {
            // Извлекаем имя сущности из пути
            var segments = path.Split('/', StringSplitOptions.RemoveEmptyEntries);
            if (segments.Length > 3)
            {
                var entityName = segments[3]; // Получаем сущность из 4-го сегмента (индекс 3)
                var queryParameters = queryString.HasValue ? queryString.Value : string.Empty;

                // Устанавливаем RouteData для контроллера и действия
                context.RouteData.Values["controller"] = "Insights";
                context.RouteData.Values["action"] = "GetTest"; // Вызываем нужный метод
                context.RouteData.Values["entity"] = entityName; // Передаем имя сущности
                context.RouteData.Values["queryParameters"] = queryParameters; // Если нужно, передаем параметры запроса

                context.HttpContext.Request.Path = new PathString("/api/v1/Insights/");
                context.HttpContext.Request.QueryString = new QueryString();

                // Передаем запрос дальше на обработку
                await _defaultRouter.RouteAsync(context);
                return;
            }
        }

        // Если маршрутизация не совпала, передаем запрос дальше
        await _defaultRouter.RouteAsync(context);
    }

    public VirtualPathData GetVirtualPath(VirtualPathContext context)
    {
        return _defaultRouter.GetVirtualPath(context);
    }
}

Обновление метода действия в контроллере

Убедитесь, что метод в вашем контроллере InsightsController обрабатывает необходимые параметры. У вас есть возможность добавить новые параметры, такие как entity и queryParameters, в методе:

[Route("api/v1/[controller]")]
[ApiController]
public class InsightsController : Controller
{
    [HttpGet("{entity}")]
    public async Task<IActionResult> Get(string entity, [FromQuery] string id = default)
    {
        // Здесь реализация работы с entity и id
        return Ok($"Entity: {entity}, ID: {id}");
    }
}

Регистрация маршрутизатора в Startup

Ваш способ регистрации маршрутизатора правильный, убедитесь, что он находится в правильной последовательности вызовов в методе Configure:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // Другие промежуточные обработчики...

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        // Используем пользовательский маршрутизатор
        var defaultRouter = endpoints.CreateApplicationBuilder().Build();
        endpoints.MapControllers(); // Обязательно для маршрутизации контроллеров

        // Регистрация пользовательского маршрутизатора
        endpoints.MapRouter(new CustomRouter(defaultRouter));
    });
}

Примечания

  1. Убедитесь, что путь к вашим конечным точкам правильно передается, и что маршрутизатор получает корректные запросы.
  2. Проверьте настройки CORS, если ваше API запрашивается с другого домена.
  3. Если вы все еще не видите ожидаемого поведения, вы можете добавить точки останова или сообщения в журнал, чтобы проверить, где маршрутизатор может не обрабатывать запросы как ожидалось.

Надеюсь, эти изменения помогут вам правильно настроить маршрутизацию в вашем ASP.NET Core 8 Web API проекте и решить вашу проблему!

Оцените материал
Добавить комментарий

Капча загружается...