Programy obsługi tras w minimalnych aplikacjach interfejsu API

Skonfigurowana WebApplication metoda obsługuje Map{Verb} metodę HTTP o wielkości liter w języku Pascal, {Verb}MapMethods na przykład Get, PostPut lub 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();

Argumenty Delegate przekazywane do tych metod są nazywane "procedurami obsługi tras".

Programy obsługi tras

Programy obsługi tras to metody, które są wykonywane, gdy trasa jest zgodna. Programy obsługi tras mogą być wyrażeniem lambda, funkcją lokalną, metodą wystąpienia lub metodą statyczną. Programy obsługi tras mogą być synchroniczne lub asynchroniczne.

Wyrażenie lambda

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();

Funkcja lokalna

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

string LocalFunction() => "This is local function";

app.MapGet("/", LocalFunction);

app.Run();

Metoda wystąpienia

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";
    }
}

Metoda statyczna

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";
    }
}

Punkt końcowy zdefiniowany poza Program.cs

Minimalne interfejsy API nie muszą znajdować się w lokalizacji 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" });
        });
    }
}

Zobacz również Grupy tras w dalszej części tego artykułu.

Punkty końcowe mogą mieć nazwy w celu wygenerowania adresów URL do punktu końcowego. Użycie nazwanego punktu końcowego pozwala uniknąć konieczności stosowania twardych ścieżek kodu w aplikacji:

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();

Powyższy kod jest wyświetlany The link to the hello endpoint is /hello z punktu końcowego / .

UWAGA: W nazwach punktów końcowych jest rozróżniana wielkość liter.

Nazwy punktów końcowych:

  • Musi ona być unikatowa w skali globalnej.
  • Są używane jako identyfikator operacji interfejsu OpenAPI, gdy jest włączona obsługa interfejsu OpenAPI. Aby uzyskać więcej informacji, zobacz OpenAPI.

Parametry trasy

Parametry trasy można przechwycić w ramach definicji wzorca trasy:

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();

Powyższy kod zwraca The user id is 3 and book id is 7 z identyfikatora URI /users/3/books/7.

Procedura obsługi tras może zadeklarować parametry do przechwycenia. Po wysłaniu żądania do trasy z zadeklarowanymi parametrami do przechwycenia parametry są analizowane i przekazywane do programu obsługi. Ułatwia to przechwytywanie wartości w bezpieczny sposób typu. W poprzednim kodzie userId i bookId mają wartość int.

W poprzednim kodzie, jeśli nie można przekonwertować żadnej wartości trasy na intwartość , zgłaszany jest wyjątek. Żądanie /users/hello/books/3 GET zgłasza następujący wyjątek:

BadHttpRequestException: Failed to bind parameter "int userId" from "hello".

Symbol wieloznaczny i przechwyć wszystkie trasy

Następujące przechwycenie wszystkich tras zwracanych Routing to hello z punktu końcowego "/posts/hello":

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");

app.Run();

Ograniczenia trasy

Ograniczenia trasy ograniczają zgodne zachowanie trasy.

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();

W poniższej tabeli przedstawiono powyższe szablony tras i ich zachowanie:

Szablon trasy Przykładowy pasujący identyfikator URI
/todos/{id:int} /todos/1
/todos/{text} /todos/something
/posts/{slug:regex(^[a-z0-9_-]+$)} /posts/mypost

Aby uzyskać więcej informacji, zobacz Route constraint reference in Routing in ASP.NET Core (Dokumentacja ograniczeń tras w usłudze Routing w usłudze ASP.NET Core).

Grupy tras

Metoda MapGroup rozszerzenia ułatwia organizowanie grup punktów końcowych za pomocą wspólnego prefiksu. Zmniejsza powtarzalny kod i umożliwia dostosowywanie całych grup punktów końcowych za pomocą jednego wywołania metod, takich jak RequireAuthorization i WithMetadata które dodają metadane punktu końcowego.

Na przykład poniższy kod tworzy dwie podobne grupy punktów końcowych:

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;
}

W tym scenariuszu możesz użyć względnego adresu nagłówka Location w 201 Created wyniku:

public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
    await database.AddAsync(todo);
    await database.SaveChangesAsync();

    return TypedResults.Created($"{todo.Id}", todo);
}

Pierwsza grupa punktów końcowych będzie pasowała tylko do żądań z prefiksem /public/todos i jest dostępna bez żadnego uwierzytelniania. Druga grupa punktów końcowych będzie pasowała tylko do żądań poprzedzonych prefiksem /private/todos i wymaga uwierzytelniania.

QueryPrivateTodosFabryka filtrów punktów końcowych to funkcja lokalna, która modyfikuje parametry programu obsługi TodoDb tras, aby umożliwić dostęp do prywatnych danych zadań do wykonania i przechowywanie ich.

Grupy tras obsługują również grupy zagnieżdżone i złożone wzorce prefiksów z parametrami i ograniczeniami trasy. W poniższym przykładzie program obsługi tras zamapowany na grupę user może przechwytywać {org} parametry trasy i {group} zdefiniowane w prefiksach grup zewnętrznych.

Prefiks może być również pusty. Może to być przydatne w przypadku dodawania metadanych lub filtrów punktu końcowego do grupy punktów końcowych bez zmiany wzorca trasy.

var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");

Dodanie filtrów lub metadanych do grupy działa tak samo jak dodawanie ich indywidualnie do każdego punktu końcowego przed dodaniem dodatkowych filtrów lub metadanych, które mogły zostać dodane do grupy wewnętrznej lub określonego punktu końcowego.

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);
});

W powyższym przykładzie zewnętrzny filtr będzie rejestrować żądanie przychodzące przed filtrem wewnętrznym, mimo że został dodany w sekundzie. Ponieważ filtry zostały zastosowane do różnych grup, kolejność, którą zostały dodane względem siebie, nie ma znaczenia. Filtry kolejności są dodawane niezależnie od tego, czy są stosowane do tej samej grupy lub określonego punktu końcowego.

Żądanie, aby zarejestrować /outer/inner/ następujące elementy:

/outer group filter
/inner group filter
MapGet filter

Powiązanie parametrów

Powiązanie parametrów w aplikacjach interfejsu API minimalnej opisuje szczegółowo reguły dotyczące sposobu wypełniania parametrów procedury obsługi tras.

Odpowiedzi

W temacie Tworzenie odpowiedzi w aplikacjach interfejsu API opisano szczegółowo sposób konwertowania wartości zwracanych z procedur obsługi tras na odpowiedzi.