Обработчики маршрутов в минимальных приложениях API
Примечание.
Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 9 этой статьи.
Предупреждение
Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в статье о политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 8 этой статьи.
Внимание
Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
В текущем выпуске см . версию .NET 9 этой статьи.
Настроенный WebApplication
метод поддерживает Map{Verb}
и MapMethods где {Verb}
используется метод HTTP с регистром Pascal, например Get
, Post
Put
илиDelete
:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "This is a GET");
app.MapPost("/", () => "This is a POST");
app.MapPut("/", () => "This is a PUT");
app.MapDelete("/", () => "This is a DELETE");
app.MapMethods("/options-or-head", new[] { "OPTIONS", "HEAD" },
() => "This is an options or head request ");
app.Run();
Аргументы Delegate , передаваемые этим методам, называются обработчиками маршрутов.
Обработчики маршрутов
Обработчики маршрутов — это методы, которые выполняются при обнаружении соответствия для маршрута. В роли обработчика маршрут может выступать лямбда-выражение, локальная функция, метод экземпляра или статический метод. Обработчики маршрутов могут быть синхронными или асинхронными.
Лямбда-выражение
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/inline", () => "This is an inline lambda");
var handler = () => "This is a lambda variable";
app.MapGet("/", handler);
app.Run();
Локальная функция
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string LocalFunction() => "This is local function";
app.MapGet("/", LocalFunction);
app.Run();
Метод экземпляра
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var handler = new HelloHandler();
app.MapGet("/", handler.Hello);
app.Run();
class HelloHandler
{
public string Hello()
{
return "Hello Instance method";
}
}
Статический метод
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", HelloHandler.Hello);
app.Run();
class HelloHandler
{
public static string Hello()
{
return "Hello static method";
}
}
Конечная точка, определенная вне Program.cs
Минимальные API не должны находиться в Program.cs
.
Program.cs
using MinAPISeparateFile;
var builder = WebApplication.CreateSlimBuilder(args);
var app = builder.Build();
TodoEndpoints.Map(app);
app.Run();
TodoEndpoints.cs
namespace MinAPISeparateFile;
public static class TodoEndpoints
{
public static void Map(WebApplication app)
{
app.MapGet("/", async context =>
{
// Get all todo items
await context.Response.WriteAsJsonAsync(new { Message = "All todo items" });
});
app.MapGet("/{id}", async context =>
{
// Get one todo item
await context.Response.WriteAsJsonAsync(new { Message = "One todo item" });
});
}
}
См. также группы маршрутов далее в этой статье.
Именованные конечные точки и создание ссылок
Конечные точки можно указать имена для создания URL-адресов конечной точки. Использование именованной конечной точки позволяет избежать сложных путей кода в приложении:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/hello", () => "Hello named route")
.WithName("hi");
app.MapGet("/", (LinkGenerator linker) =>
$"The link to the hello route is {linker.GetPathByName("hi", values: null)}");
app.Run();
Приведенный выше код отображает The link to the hello route is /hello
из конечной точки /
.
ПРИМЕЧАНИЕ. Имена конечных точек чувствительны к регистру.
Имена конечных точек:
- Оно должно быть глобально уникальным.
- используются в качестве идентификатора операции OpenAPI, если включена поддержка OpenAPI. Дополнительные сведения см. в статье OpenAPI.
Параметры маршрута
Параметры маршрута могут быть захвачены в составе определения шаблона маршрута:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/users/{userId}/books/{bookId}",
(int userId, int bookId) => $"The user id is {userId} and book id is {bookId}");
app.Run();
Приведенный выше код возвращает The user id is 3 and book id is 7
из URI /users/3/books/7
.
Обработчик маршрута может объявлять параметры, которые нужно захватывать. Когда запрос выполняется в маршрут с параметрами, объявленными для записи, параметры анализируются и передаются обработчику. Это позволяет легко получать значения в строго типизированном виде. В приведенном выше коде userId
и bookId
имеют тип int
.
В приведенном выше коде создается исключение, если значение маршрута не может быть преобразовано в тип int
. Запрос GET по маршруту /users/hello/books/3
выдает следующее исключение:
BadHttpRequestException: Failed to bind parameter "int userId" from "hello".
Использование подстановочных знаков и перехват всех маршрутов
Следующая функция перехвата всех маршрутов возвращает значение Routing to hello
из конечной точки "/posts/hello":
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
app.Run();
Ограничения маршрута
Ограничения маршрута ограничивают возможности сопоставления маршрута.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/todos/{id:int}", (int id) => db.Todos.Find(id));
app.MapGet("/todos/{text}", (string text) => db.Todos.Where(t => t.Text.Contains(text));
app.MapGet("/posts/{slug:regex(^[a-z0-9_-]+$)}", (string slug) => $"Post {slug}");
app.Run();
В приведенной ниже таблице перечислены представленные выше примеры шаблонов маршрутов и их поведение.
Шаблон маршрута | Пример соответствующего URI |
---|---|
/todos/{id:int} |
/todos/1 |
/todos/{text} |
/todos/something |
/posts/{slug:regex(^[a-z0-9_-]+$)} |
/posts/mypost |
Дополнительные сведения см. в разделе Справочник по ограничениям маршрутов в статье Маршрутизация в ASP.NET Core.
Группы маршрутов
Метод MapGroup расширения помогает упорядочивать группы конечных точек с общим префиксом. Это уменьшает повторяющийся код и позволяет настраивать целые группы конечных точек с одним вызовом методов, таких как RequireAuthorization и WithMetadata которые добавляют метаданные конечной точки.
Например, следующий код создает две аналогичные группы конечных точек:
app.MapGroup("/public/todos")
.MapTodosApi()
.WithTags("Public");
app.MapGroup("/private/todos")
.MapTodosApi()
.WithTags("Private")
.AddEndpointFilterFactory(QueryPrivateTodos)
.RequireAuthorization();
EndpointFilterDelegate QueryPrivateTodos(EndpointFilterFactoryContext factoryContext, EndpointFilterDelegate next)
{
var dbContextIndex = -1;
foreach (var argument in factoryContext.MethodInfo.GetParameters())
{
if (argument.ParameterType == typeof(TodoDb))
{
dbContextIndex = argument.Position;
break;
}
}
// Skip filter if the method doesn't have a TodoDb parameter.
if (dbContextIndex < 0)
{
return next;
}
return async invocationContext =>
{
var dbContext = invocationContext.GetArgument<TodoDb>(dbContextIndex);
dbContext.IsPrivate = true;
try
{
return await next(invocationContext);
}
finally
{
// This should only be relevant if you're pooling or otherwise reusing the DbContext instance.
dbContext.IsPrivate = false;
}
};
}
public static RouteGroupBuilder MapTodosApi(this RouteGroupBuilder group)
{
group.MapGet("/", GetAllTodos);
group.MapGet("/{id}", GetTodo);
group.MapPost("/", CreateTodo);
group.MapPut("/{id}", UpdateTodo);
group.MapDelete("/{id}", DeleteTodo);
return group;
}
В этом сценарии можно использовать относительный адрес заголовка Location
201 Created
в результате:
public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
await database.AddAsync(todo);
await database.SaveChangesAsync();
return TypedResults.Created($"{todo.Id}", todo);
}
Первая группа конечных точек будет соответствовать только запросам, префиксным и /public/todos
доступным без какой-либо проверки подлинности. Вторая группа конечных точек будет соответствовать только запросам, префиксным и /private/todos
требующим проверки подлинности.
Фабрика QueryPrivateTodos
фильтров конечных точек — это локальная функция, которая изменяет параметры обработчика TodoDb
маршрутов, чтобы разрешить доступ к частным данным и хранить данные о частных объектах.
Группы маршрутов также поддерживают вложенные группы и сложные шаблоны префикса с параметрами маршрута и ограничениями. В следующем примере обработчик маршрутов, сопоставленный с user
группой, может записывать {org}
параметры маршрута, {group}
определенные в префиксах внешней группы.
Префикс также может быть пустым. Это может быть полезно для добавления метаданных конечной точки или фильтров в группу конечных точек без изменения шаблона маршрута.
var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");
Добавление фильтров или метаданных в группу ведет себя так же, как и их отдельно к каждой конечной точке перед добавлением дополнительных фильтров или метаданных, которые могли быть добавлены во внутреннюю группу или определенную конечную точку.
var outer = app.MapGroup("/outer");
var inner = outer.MapGroup("/inner");
inner.AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("/inner group filter");
return next(context);
});
outer.AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("/outer group filter");
return next(context);
});
inner.MapGet("/", () => "Hi!").AddEndpointFilter((context, next) =>
{
app.Logger.LogInformation("MapGet filter");
return next(context);
});
В приведенном выше примере внешний фильтр регистрирует входящий запрос до внутреннего фильтра, даже если он был добавлен вторым. Так как фильтры были применены к разным группам, порядок их добавления относительно друг друга не имеет значения. Фильтры заказов добавляются, если они применяются к той же группе или определенной конечной точке.
Запрос, который /outer/inner/
будет регистрировать следующее:
/outer group filter
/inner group filter
MapGet filter
Привязка параметра
Привязка параметров в минимальных приложениях API подробно описывает правила заполнения параметров обработчика маршрутов.
Отклики
Создание ответов в минимальных приложениях API подробно описывает, как значения, возвращаемые обработчиками маршрутов, преобразуются в ответы.
ASP.NET Core