Привет,
Хочу разобраться с роутингом (маршрутизацией) в .net приложениях
Недавно появились minimal APIs и там роутинг строится так:
app.MapGet("/", () => "Hello World!");
Ранее в .net framework была маршрутизация на основе соглашений (conventional), мы добавляли контроллеры (или контроллеры с представлениями), подключали роутинг и прописывали маппинги всё в том же стартапе:
builder.Services.AddControllersWithViews();
<...>
app.UseRouting();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
// или
// app.MapDefaultControllerRoute();
Затем, в .net core стали переходить к роутингу на атрибутах, при этом можно было сохранить роутинг на основе соглашений, типа такого:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
<...>
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
С обычным api должно быть что-то вроде такого:
services.AddControllers();
<...>
app.MapControllers();
Ну и стали ставить атрибуты на каждый контроллер и каждый action, что-то вроде такого:
[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
[HttpGet] // GET /api/test2
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")] // GET /api/test2/xyz
public IActionResult GetProduct(string id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpGet("int/{id:int}")] // GET /api/test2/int/3
public IActionResult GetIntProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpGet("int2/{id}")] // GET /api/test2/int2/3
public IActionResult GetInt2Product(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Вопрос, исторически из-за чего произошел такой переход, насколько понимаю, сейчас стандартом идет роутинг через атрибуты, как-то я упустил статьи по этому поводу.
Так же интересуют другие способы, хочу разобраться в вопросе, так сказать "раз и навсегда".
Я увидел в чужих проектах, например, можно выставить какие-то эндпоинты наружу вот такими способами:
app
.UseHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready"),
})
.UseHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = _ => false,
});
app.UseEndpoints(
c => c.MapControllers()
);
// да тот же сваггер добавляет свой эндпоинт
app.UseSwaggerUI();
Еще можно навешивать свои какие-то методы прям на эндпоинты, типа такого:
public const string StopConsumerPath = "/kafka/stopconsumer";
public const string StartConsumerPath = "/kafka/startconsumer";
public const string RewindOffsetPath = "/kafka/rewindoffset";
public static void MapKafkaConsumersTools(this IEndpointRouteBuilder endpoints)
{
endpoints.MapEndpoint<KafkaConsumersStopRequest>(
StopConsumerPath,
(request, rewindService, ct) => rewindService.StopConsumeFor(request.Topic, ct));
endpoints.MapEndpoint<KafkaConsumersStartRequest>(
StartConsumerPath,
(request, rewindService, ct) => rewindService.StartConsumerFor(request.Topic, ct));
endpoints.MapEndpoint<KafkaConsumersRewindRequest>(
RewindOffsetPath,
(request, rewindService, ct) =>
{
rewindService.RewindTopic(request.Topic, request.RewindTo, request.PartitionsCount, ct);
return Task.CompletedTask;
});
}
private static void MapEndpoint<TRequest>(
this IEndpointRouteBuilder endpoints,
string path,
Func<TRequest, RewindService, CancellationToken, Task> execute)
{
endpoints.MapPost(path, async context =>
{
TRequest request;
var logger = context.RequestServices.GetService<ILogger<RewindService>>();
var ct = context.RequestAborted;
try
{
request = await ParseRequest<TRequest>(context);
}
catch (Exception e)
{
logger?.LogError(e, "Error handling request {Path}", path);
await SetErrorResponse(context.Response, HttpStatusCode.BadRequest, e, ct);
return;
}
try
{
var rewindService = context.RequestServices.GetRequiredService<RewindService>();
await execute(request, rewindService, ct);
SetSuccessResponse(context.Response);
}
catch (Exception e)
{
logger?.LogError(e, "Error handling request {Path}", path);
await SetErrorResponse(context.Response, HttpStatusCode.InternalServerError, e, ct);
}
});
}
Хотя в статьях про роутинг об этом ни слова. Подскажите, что почитать или посмотреть по данному вопросу, чтобы вот всю тему разобрать?