Bagikan melalui


Penangan Rute di aplikasi API Minimal

Catatan

Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Peringatan

Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Penting

Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.

Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Dukungan yang dikonfigurasi WebApplication dan MapMethods di mana {Verb} adalah metode HTTP berkode Pascal seperti Get, , PostPut atau Delete:Map{Verb}

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

Argumen Delegate yang diteruskan ke metode ini disebut "penangan rute".

Penangan rute

Penangan rute adalah metode yang dijalankan saat rute cocok. Penangan rute dapat berupa ekspresi lambda, fungsi lokal, metode instans, atau metode statis. Penangan rute bisa sinkron atau asinkron.

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

Fungsi lokal

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

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

app.MapGet("/", LocalFunction);

app.Run();

Metode instans

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

Metode statis

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

Titik akhir yang ditentukan di luar Program.cs

API minimal tidak harus berada di 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" });
        });
    }
}

Lihat juga Merutekan grup nanti di artikel ini.

Titik akhir dapat diberikan nama untuk menghasilkan URL ke titik akhir. Menggunakan titik akhir bernama menghindari harus melakukan jalur kode keras di aplikasi:

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

Kode sebelumnya ditampilkan The link to the hello route is /hello dari / titik akhir.

CATATAN: Nama titik akhir peka huruf besar/kecil.

Nama titik akhir:

  • Harus unik secara global.
  • Digunakan sebagai id operasi OpenAPI saat dukungan OpenAPI diaktifkan. Untuk informasi selengkapnya, lihat OpenAPI.

Parameter Rute

Parameter rute dapat ditangkap sebagai bagian dari definisi pola rute:

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

Kode sebelumnya dikembalikan The user id is 3 and book id is 7 dari URI /users/3/books/7.

Handler rute dapat mendeklarasikan parameter yang akan diambil. Ketika permintaan dibuat ke rute dengan parameter yang dinyatakan untuk diambil, parameter diurai dan diteruskan ke handler. Ini memudahkan untuk menangkap nilai dengan cara yang aman. Dalam kode sebelumnya, userId dan bookId keduanya int.

Dalam kode sebelumnya, jika salah satu nilai rute tidak dapat dikonversi ke int, pengecualian akan dilemparkan. Permintaan /users/hello/books/3 GET melemparkan pengecualian berikut:

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

Kartubebas dan tangkap semua rute

Berikut ini menangkap semua rute yang Routing to hello dikembalikan dari titik akhir '/posts/hello':

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

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

app.Run();

Batasan rute

Batasan rute membatasi perilaku pencocokan rute.

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

Tabel berikut menunjukkan templat rute sebelumnya dan perilakunya:

Templat Rute Contoh URI yang Cocok
/todos/{id:int} /todos/1
/todos/{text} /todos/something
/posts/{slug:regex(^[a-z0-9_-]+$)} /posts/mypost

Untuk informasi selengkapnya, lihat Referensi batasan rute dalam Perutean di ASP.NET Core.

Grup rute

Metode MapGroup ekstensi membantu mengatur grup titik akhir dengan awalan umum. Ini mengurangi kode berulang dan memungkinkan untuk menyesuaikan seluruh grup titik akhir dengan satu panggilan ke metode seperti RequireAuthorization dan WithMetadata yang menambahkan metadata titik akhir.

Misalnya, kode berikut membuat dua grup titik akhir serupa:

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

Dalam skenario ini, Anda dapat menggunakan alamat relatif untuk Location header dalam hasil 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);
}

Grup titik akhir pertama hanya akan cocok dengan permintaan yang diawali dan /public/todos dapat diakses tanpa autentikasi apa pun. Grup titik akhir kedua hanya akan cocok dengan permintaan yang diawali dan /private/todos memerlukan autentikasi.

Pabrik QueryPrivateTodos filter titik akhir adalah fungsi lokal yang memodifikasi parameter handler TodoDb rute untuk memungkinkan mengakses dan menyimpan data todo privat.

Grup rute juga mendukung grup berlapis dan pola awalan kompleks dengan parameter rute dan batasan. Dalam contoh berikut, dan handler rute yang dipetakan user ke grup dapat menangkap {org} parameter rute dan {group} yang ditentukan dalam awalan grup luar.

Awalan juga dapat kosong. Ini dapat berguna untuk menambahkan metadata titik akhir atau filter ke sekelompok titik akhir tanpa mengubah pola rute.

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

Menambahkan filter atau metadata ke grup berulah dengan cara yang sama seperti menambahkannya satu per satu ke setiap titik akhir sebelum menambahkan filter atau metadata tambahan yang mungkin telah ditambahkan ke grup dalam atau titik akhir tertentu.

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

Dalam contoh di atas, filter luar akan mencatat permintaan masuk sebelum filter dalam meskipun ditambahkan kedua. Karena filter diterapkan ke grup yang berbeda, urutan yang ditambahkan relatif satu sama lain tidak masalah. Filter pesanan ditambahkan tidak masalah jika diterapkan ke grup yang sama atau titik akhir tertentu.

Permintaan untuk /outer/inner/ mencatat hal berikut:

/outer group filter
/inner group filter
MapGet filter

Pengikatan parameter

Pengikatan parameter dalam aplikasi API Minimal menjelaskan aturan secara rinci tentang bagaimana parameter handler rute diisi.

Respons

Membuat respons dalam aplikasi API Minimal menjelaskan secara rinci bagaimana nilai yang dikembalikan dari handler rute dikonversi menjadi respons.