Вопрос или проблема
В моём проекте 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));
});
}
Примечания
- Убедитесь, что путь к вашим конечным точкам правильно передается, и что маршрутизатор получает корректные запросы.
- Проверьте настройки CORS, если ваше API запрашивается с другого домена.
- Если вы все еще не видите ожидаемого поведения, вы можете добавить точки останова или сообщения в журнал, чтобы проверить, где маршрутизатор может не обрабатывать запросы как ожидалось.
Надеюсь, эти изменения помогут вам правильно настроить маршрутизацию в вашем ASP.NET Core 8 Web API проекте и решить вашу проблему!