최소 API 앱의 경로 처리기

구성된 WebApplicationMap{Verb} 지원 및 MapMethods{Verb} 스칼 대/소문자 HTTP 메서드(예: Get또는 PostPutDelete:

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 endpoint is /hello를 표시합니다.

참고: 엔드포인트 이름은 대/소문자를 구분합니다.

엔드포인트 이름:

  • 전역적으로 고유해야 합니다.
  • OpenAPI 지원을 사용할 때 OpenAPI 작업 ID로 사용됩니다. 자세한 내용은 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();

이전 코드는 /users/3/books/7 URI에서 The user id is 3 and book id is 7을 반환합니다.

경로 처리기는 캡처할 매개 변수를 선언할 수 있습니다. 캡처하도록 선언된 매개 변수가 있는 경로에 대한 요청이 이루어지면 매개 변수가 구문 분석되어 처리기에 전달됩니다. 이렇게 하면 형식이 안전한 방식으로 값을 쉽게 캡처할 수 있습니다. 이전 코드에서 userIdbookId는 모두 int입니다.

이전 코드에서 경로 값 중 하나를 int로 변환할 수 없는 경우 예외가 throw됩니다. GET 요청 /users/hello/books/3는 다음 예외를 throw합니다.

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

와일드카드 및 catch all 경로

다음 catch all 경로는 `/posts/hello` 엔드포인트에서 Routing to 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;
}

이 시나리오에서는 201 Created 결과에서 Location 헤더에 대한 상대 주소를 사용할 수 있습니다.

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 애플리케이션에서 응답 만들기 는 경로 처리기에서 반환된 값이 응답으로 변환되는 방법을 자세히 설명합니다.