다음을 통해 공유


OpenAPI 문서 생성

이 패키지는 Microsoft.AspNetCore.OpenApi ASP.NET Core에서 OpenAPI 문서 생성을 기본적으로 지원합니다. 패키지는 다음과 같은 기능을 제공합니다.

  • 런타임에 OpenAPI 문서를 생성하고 애플리케이션의 엔드포인트를 통해 액세스할 수 있도록 지원합니다.
  • 생성된 문서를 수정할 수 있는 "변환기" API를 지원합니다.
  • 단일 앱에서 여러 OpenAPI 문서 생성을 지원합니다.
  • 에서 제공하는 System.Text.JsonJSON 스키마 지원을 활용합니다.
  • 네이티브 AoT와 호환됩니다.

패키지 설치

Microsoft.AspNetCore.OpenApi 패키지를 설치합니다.

패키지 관리자 콘솔에서 다음 명령을 실행합니다.

Install-Package Microsoft.AspNetCore.OpenApi -IncludePrerelease

빌드 시 OpenAPI 문서 생성에 대한 지원을 추가하려면 패키지를 설치합니다 Microsoft.Extensions.ApiDescription.Server .

패키지 관리자 콘솔에서 다음 명령을 실행합니다.

Install-Package Microsoft.Extensions.ApiDescription.Server -IncludePrerelease

OpenAPI 문서 생성 구성

코드는 다음과 같습니다.

  • OpenAPI 서비스를 추가합니다.
  • 엔드포인트가 JSON 형식으로 OpenAPI 문서를 볼 수 있도록 합니다.
var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => "Hello world!");

app.Run();

앱을 시작하고 생성된 OpenAPI 문서를 보려면 이동합니다 https://localhost:<port>/openapi/v1.json .

ASP.NET 웹앱에 OpenAPI 메타데이터 포함

엔드포인트에 대한 OpenAPI 메타데이터 포함

ASP.NET 웹앱의 엔드포인트에서 메타데이터를 수집하고 이를 사용하여 OpenAPI 문서를 생성합니다. 컨트롤러 기반 앱에서 메타데이터는 < [HttpPost]a0/>와 같은 [EndpointDescription]특성에서 수집됩니다[Produces]. 최소 API에서는 특성에서 메타데이터를 수집할 수 있지만 확장 메서드 및 경로 처리기에서 반환하는 TypedResults 등의 기타 전략을 사용하여 설정할 수도 있습니다. 다음 표에서는 수집된 메타데이터 및 설정 전략에 대한 개요를 제공합니다.

메타데이터 attribute 확장 메서드 기타 전략
요약 [EndpointSummary] WithSummary
description [EndpointDescription] WithDescription
tags [Tags] WithTags
operationId [EndpointName] WithName
매개 변수 [FromQuery], [FromRoute], [FromHeader][FromForm]
매개 변수 설명 [EndpointDescription]
requestBody [FromBody] Accepts
응답 [Produces] Produces, ProducesProblem TypedResults
엔드포인트 제외 [ExcludeFromDescription], [ApiExplorerSettings] ExcludeFromDescription

ASP.NET Core는 XML 문서 주석에서 메타데이터를 수집하지 않습니다.

다음 섹션에서는 생성된 OpenAPI 문서를 사용자 지정하기 위해 앱에 메타데이터를 포함하는 방법을 보여 줍니다.

요약 및 설명

엔드포인트 요약 및 설명은 및 [EndpointDescription] 특성 또는 최소 API에서 확장 메서드 및 WithDescription 확장 메서드를 사용하여 WithSummary 설정할 [EndpointSummary] 수 있습니다.

다음 샘플에서는 요약 및 설명을 설정하기 위한 다양한 전략을 보여 줍니다.

특성은 앱이 아닌 대리자 메서드에 배치됩니다. MapGet 메서드.

app.MapGet("/extension-methods", () => "Hello world!")
  .WithSummary("This is a summary.")
  .WithDescription("This is a description.");

app.MapGet("/attributes",
  [EndpointSummary("This is a summary.")]
  [EndpointDescription("This is a description.")]
  () => "Hello world!");

tags

OpenAPI는 각 엔드포인트에서 태그를 분류의 형태로 지정할 수 있습니다.

최소 API에서는 특성 또는 확장 메서드를 [Tags] 사용하여 태그를 WithTags 설정할 수 있습니다.

다음 샘플에서는 태그를 설정하기 위한 다양한 전략을 보여 줍니다.

app.MapGet("/extension-methods", () => "Hello world!")
  .WithTags("todos", "projects");

app.MapGet("/attributes",
  [Tags("todos", "projects")]
  () => "Hello world!");

operationId

OpenAPI는 각 엔드포인트에서 operationId를 작업의 고유 식별자 또는 이름으로 지원합니다.

최소 API에서 operationId는 특성 또는 WithName 확장 메서드를 [EndpointName] 사용하여 설정할 수 있습니다.

다음 샘플에서는 operationId를 설정하기 위한 다양한 전략을 보여 줍니다.

app.MapGet("/extension-methods", () => "Hello world!")
  .WithName("FromExtensionMethods");

app.MapGet("/attributes",
  [EndpointName("FromAttributes")]
  () => "Hello world!");

매개 변수

OpenAPI는 API에서 사용하는 경로, 쿼리 문자열, 헤더 및 cookie 매개 변수에 주석을 추가합니다.

프레임워크는 경로 처리기의 서명에 따라 요청 매개 변수의 형식을 자동으로 유추합니다.

[EndpointDescription] 특성을 사용하여 매개 변수에 대한 설명을 제공할 수 있습니다.

다음 샘플에서는 매개 변수에 대한 설명을 설정하는 방법을 보여 줍니다.

app.MapGet("/attributes",
  ([Description("This is a description.")] string name) => "Hello world!");

requestBody

요청 본문으로 전송되는 입력 유형을 정의하려면 Accepts 확장 메서드를 사용하여 요청 처리기에서 예상하는 개체 형식 및 콘텐츠 형식을 정의하여 속성을 구성합니다. 다음 예제에서 엔드포인트는 요청 본문에 예상되는 콘텐츠 형식이 application/xmlTodo 개체를 허용합니다.

app.MapPost("/todos/{id}", (int id, Todo todo) => ...)
  .Accepts<Todo>("application/xml");

확장 메서드 외에도 Accepts 매개 변수 형식은 인터페이스를 구현하여 자체 주석을 IEndpointParameterMetadataProvider 설명할 수 있습니다. 예를 들어, 다음 Todo 형식은 application/xml 콘텐츠 형식의 요청 본문이 필요한 주석을 추가합니다.

public class Todo : IEndpointParameterMetadataProvider
{
    public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
    {
        builder.Metadata.Add(new AcceptsMetadata(["application/xml", "text/xml"], typeof(XmlBody)));
    }
}

명시적 주석이 제공되지 않으면 프레임워크는 엔드포인트 처리기에 요청 본문 매개 변수가 있는 경우 기본 요청 유형을 확인하려고 시도합니다. 유추는 다음 추론을 사용하여 주석을 생성합니다.

  • [FromForm] 특성을 통해 양식에서 읽은 요청 본문 매개 변수는 multipart/form-data 콘텐츠 형식으로 설명됩니다.
  • 다른 모든 요청 본문 매개 변수는 application/json 콘텐츠 형식으로 설명됩니다.
  • 요청 본문은 nullable이거나 AllowEmpty 속성이 [FromBody] 특성에 설정된 경우 선택 사항으로 처리됩니다.

응답 형식 설명

OpenAPI는 API에서 반환된 응답에 대한 설명을 제공할 수 있습니다. ASP.NET Core는 엔드포인트의 응답 메타데이터를 설정하기 위한 몇 가지 전략을 제공합니다. 설정할 수 있는 응답 메타데이터에는 상태 코드, 응답 본문의 형식 및 응답의 콘텐츠 형식이 포함됩니다. OpenAPI의 응답에는 설명, 헤더, 링크 및 예제와 같은 추가 메타데이터가 있을 수 있습니다. 이 추가 메타데이터는 문서 변환기 또는 작업 변환기를 사용하여 설정할 수 있습니다.

응답 메타데이터를 설정하기 위한 특정 메커니즘은 개발 중인 앱의 유형에 따라 달라집니다.

최소 API 앱에서 ASP.NET Core는 엔드포인트의 확장 메서드, 경로 처리기의 특성 및 경로 처리기의 반환 형식에 의해 추가된 응답 메타데이터를 추출할 수 있습니다.

  • Produces 엔드포인트에서 확장 메서드를 사용하여 엔드포인트에서 응답의 상태 코드, 응답 본문 형식 및 콘텐츠 형식을 지정할 수 있습니다.
  • [ProducesResponseType] 또는 ProducesResponseTypeAttribute 특성을 사용하여 응답 본문의 형식을 지정할 수 있습니다.
  • 경로 처리기를 사용하여 응답 본문의 형식 및 콘텐츠 형식을 IEndpointMetadataProvider 지정하기 위해 구현하는 형식을 반환할 수 있습니다.
  • ProducesProblem 엔드포인트의 확장 메서드를 사용하여 오류 응답의 상태 코드 및 콘텐츠 형식을 지정할 수 있습니다.

확장 Produces 메서드와 ProducesProblem 확장 메서드는 둘 다 RouteHandlerBuilder 에서 RouteGroupBuilder지원됩니다. 예를 들어 그룹의 모든 작업에 대해 일반적인 오류 응답 집합을 정의할 수 있습니다.

이전 전략 중 하나로 지정하지 않은 경우 다음을 수행합니다.

  • 응답의 상태 코드는 기본적으로 200입니다.
  • 응답 본문에 대한 스키마는 엔드포인트 메서드의 암시적 또는 명시적 반환 형식에서 유추할 수 있습니다( 예: in에서 T Task). 그렇지 않으면 지정되지 않은 것으로 간주됩니다.
  • 지정하거나 유추된 응답 본문의 콘텐츠 형식은 "application/json"입니다.

최소 API Produces 에서 확장 메서드와 특성은 [ProducesResponseType] 엔드포인트에 대한 응답 메타데이터만 설정합니다. 메타데이터에 지정된 것과 다른 상태 코드 또는 응답 본문 형식을 반환할 수 있는 엔드포인트의 동작을 수정하거나 제한하지 않으며, 콘텐츠 형식은 특성 또는 확장 메서드에 지정된 콘텐츠 형식에 관계없이 경로 처리기 메서드의 반환 형식에 따라 결정됩니다.

확장 메서드는 Produces 기본 상태 코드 200 및 기본 콘텐츠 형식을 사용하여 엔드포인트의 응답 형식을 지정할 수 있습니다 application/json. 다음 예제는 이러한 과정을 보여 줍니다.

app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
  .Produces<IList<Todo>>();

엔드 [ProducesResponseType] 포인트에 응답 메타데이터를 추가하는 데 사용할 수 있습니다. 특성은 다음 예제와 같이 경로를 만들기 위한 메서드 호출이 아니라 경로 처리기 메서드에 적용됩니다.

app.MapGet("/todos",
    [ProducesResponseType<List<Todo>>(200)]
    async (TodoDb db) => await db.Todos.ToListAsync());

엔드포인트의 경로 처리기 구현에서 TypedResults를 사용하면 엔드포인트에 대한 응답 형식 메타데이터가 자동으로 포함됩니다. 예를 들어, 다음 코드는 application/json 콘텐츠 형식의 200 상태 코드 아래에 응답으로 엔드포인트에 자동으로 주석을 추가합니다.

app.MapGet("/todos", async (TodoDb db) =>
{
    var todos = await db.Todos.ToListAsync();
    return TypedResults.Ok(todos);
});

OpenAPI 문서에서 항목 만들기를 구현 IEndpointMetadataProvider 하는 responses 형식만 반환합니다. 다음은 항목을 생성하는 도우미 메서드의 TypedResults 일부 목록입니다.responses

TypedResults 도우미 메서드 상태 코드
Ok() 200
Created() 201
CreatedAtRoute() 201
Accepted() 202
AcceptedAtRoute() 202
NoContent() 204
BadRequest() 400
ValidationProblem() 400
NotFound() 404
Conflict() 409
UnprocessableEntity() 422

응답 본문의 형식을 지정하는 제네릭 오버로드를 제외한 NoContent 이러한 메서드는 모두 있습니다.

클래스를 구현하여 엔드포인트 메타데이터를 설정하고 경로 처리기에서 반환할 수 있습니다.

ProblemDetails에 대한 응답 설정

ProblemDetails 응답을 반환할 수 있는 엔드포인트에 대한 응답 유형을 설정하는 경우 다음을 사용하여 엔드포인트에 대한 적절한 응답 메타데이터를 추가할 수 있습니다.

ProblemDetails 응답을 반환하도록 최소 API 앱을 구성하는 방법에 대한 자세한 내용은 최소 API의 오류 처리를 참조 하세요.

여러 응답 유형

엔드포인트가 다양한 시나리오에서 다른 응답 형식을 반환할 수 있는 경우 다음과 같은 방법으로 메타데이터를 제공할 수 있습니다.

  • 다음 예제와 같이 Produces 확장 메서드를 여러 번 호출합니다.

    app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) =>
             await db.Todos.FindAsync(id) 
             is Todo todo
             ? Results.Ok(todo) 
             : Results.NotFound())
       .Produces<Todo>(StatusCodes.Status200OK)
       .Produces(StatusCodes.Status404NotFound);
    
  • 다음 예제와 같이, 서명에 Results<TResult1,TResult2,TResultN>을 사용하고 처리기 본문에 TypedResults를 사용합니다.

    app.MapGet("/book/{id}", Results<Ok<Book>, NotFound> (int id, List<Book> bookList) =>
    {
        return bookList.FirstOrDefault((i) => i.Id == id) is Book book
         ? TypedResults.Ok(book)
         : TypedResults.NotFound();
    });
    

    공용 구조체 형식Results<TResult1,TResult2,TResultN> 경로 처리기가 구현하는 구체적인 형식을 여러 IResult번 반환하고, 구현 IEndpointMetadataProvider 하는 형식 중 하나라도 엔드포인트의 메타데이터에 기여한다고 선언합니다.

    공용 구조체 형식은 암시적 캐스트 연산자를 구현합니다. 이러한 연산자를 사용하면 컴파일러가 제네릭 인수에 지정된 형식을 공용 구조체 형식의 인스턴스로 자동으로 변환할 수 있습니다. 이 기능은 경로 처리기가 선언한 결과만 반환한다는 컴파일 타임 검사를 제공하는 추가 이점이 있습니다. 제네릭 인수 중 하나로 선언되지 않은 형식을 Results<TResult1,TResult2,TResultN>로 반환하려고 시도하면 컴파일 오류가 발생합니다.

생성된 문서에서 엔드포인트 제외

기본적으로 앱에 정의된 모든 엔드포인트는 생성된 OpenAPI 파일에 설명되어 있지만 특성 또는 확장 메서드를 사용하여 문서에서 엔드포인트를 제외할 수 있습니다.

제외해야 하는 엔드포인트를 지정하는 메커니즘은 개발 중인 앱의 유형에 따라 달라집니다.

최소 API는 OpenAPI 문서에서 지정된 엔드포인트를 제외하기 위한 두 가지 전략을 지원합니다.

다음 샘플에서는 생성된 OpenAPI 문서에서 지정된 엔드포인트를 제외하기 위한 다양한 전략을 보여 줍니다.

app.MapGet("/extension-method", () => "Hello world!")
  .ExcludeFromDescription();

app.MapGet("/attributes",
  [ExcludeFromDescription]
  () => "Hello world!");

데이터 형식에 대한 OpenAPI 메타데이터 포함

요청 또는 응답 본문에 사용되는 C# 클래스 또는 레코드는 생성된 OpenAPI 문서에서 스키마로 표시됩니다. 기본적으로 공용 속성만 스키마에 표시되지만 JsonSerializerOptions 필드에 대한 스키마 속성도 만들어야 합니다.

PropertyNamingPolicy 카멜 대/소문자(ASP.NET 웹 애플리케이션의 기본값)로 설정된 경우 스키마의 속성 이름은 클래스 또는 레코드 속성 이름의 camel 대/소문자 형식입니다. [JsonPropertyName] 개별 속성에서 스키마의 속성 이름을 지정하는 데 사용할 수 있습니다.

형식 및 형식

JSON 스키마 라이브러리는 다음과 같이 표준 C# 형식을 OpenAPI type format 에 매핑합니다.

C# 형식 OpenAPI type OpenAPI format
int 정수 int32
long 정수 int64
short 정수 int16
바이트 정수 uint8
float 번호 float
double 번호 double
decimal 번호 double
bool 부울 값
string string
char string char
byte[] string 바이트
DateTimeOffset string 날짜-시간
DateOnly string date
TimeOnly string 시간
URI string uri
GUID string uuid
개체 생략
dynamic 생략

개체 및 동적 형식은 Int 또는 문자열과 같은 기본 형식을 포함하여 모든 형식의 데이터를 포함할 수 있으므로 OpenAPI에 정의된 형식이 없습니다 .

type format 스키마 변환기를 사용하여 설정할 수도 있습니다. 예를 들어 10진수 형식이 format 대신 되도록 decimal double할 수 있습니다.

특성을 사용하여 메타데이터 추가

ASP.NET 클래스 또는 레코드 속성의 특성에서 메타데이터를 사용하여 생성된 스키마의 해당 속성에 대한 메타데이터를 설정합니다.

다음 표에는 생성된 스키마에 대한 메타데이터를 System.ComponentModel 제공하는 네임스페이스의 특성이 요약되어 있습니다.

attribute 설명
[Description] 스키마에서 description 속성의 집합입니다.
[Required] 스키마에서와 같이 required 속성을 표시합니다.
[DefaultValue] 스키마의 default 속성 값을 설정합니다.
[Range] 정수 minimum 또는 숫자의 값과 maximum 값을 설정합니다.
[MinLength] 문자열을 minLength 설정합니다.
[MaxLength] 문자열을 maxLength 설정합니다.
[RegularExpression] 문자열을 pattern 설정합니다.

컨트롤러 기반 앱에서 이러한 특성은 들어오는 데이터가 제약 조건을 충족하는지 확인하기 위해 작업에 필터를 추가합니다. 최소 API에서 이러한 특성은 생성된 스키마에서 메타데이터를 설정하지만 유효성 검사는 엔드포인트 필터, 경로 처리기 논리 또는 타사 패키지를 통해 명시적으로 수행되어야 합니다.

생성된 스키마에 대한 메타데이터의 다른 원본

필수

필요한 한정자로 속성을 표시 required 할 수도 있습니다.

enum

C#의 열거형 형식은 정수 기반이지만 a 및 a [JsonConverter] JsonStringEnumConverter를 사용하여 JSON에서 문자열로 나타낼 수 있습니다. 열거형 형식이 JSON에서 문자열로 표현되면 생성된 스키마에는 enum 열거형의 문자열 값이 있는 속성이 있습니다. 없는 열거형 형식 [JsonConverter] 은 생성된 스키마에서와 같이 type: integer 정의됩니다.

참고: [AllowedValues] 속성 값을 설정 enum 하지 않습니다.

nullable

nullable 값 또는 참조 형식으로 정의된 속성은 생성된 스키마에 있습니다 nullable: true . 이는 nullable 속성에 Json 유효한 값으로 허용하는 null 역직렬 변환기의 기본 동작과 일치합니다.

additionalProperties

스키마는 기본적으로 어설션 없이 additionalProperties 생성되며 이는 기본값 true을 의미합니다. 이는 JSON 개체의 Json 추가 속성을 자동으로 무시하는 역직렬 변환기의 기본 동작과 일치합니다.

스키마의 추가 속성에 특정 형식의 값만 있어야 하는 경우 속성 또는 클래스를 Dictionary<string, type>로 정의합니다. 사전의 키 형식은 .이어야 string합니다. 이렇게 하면 "type"에 대한 스키마를 필수 값 형식으로 지정하는 스키마 additionalProperties 가 생성됩니다.

다형 형식에 대한 메타데이터

부모 클래스의 [JsonPolymorphic] 특성 및 [JsonDerivedType] 특성을 사용하여 다형 형식에 대한 판별자 필드 및 하위 형식을 지정합니다.

[JsonDerivedType] 하위 클래스의 스키마에 판별자 필드를 추가하고 하위 클래스의 특정 판별자 값을 지정하는 열거형을 추가합니다. 또한 이 특성은 각 파생 클래스의 생성자를 수정하여 판별자 값을 설정합니다.

특성이 있는 [JsonPolymorphic] 추상 클래스에는 discriminator 스키마에 필드가 있지만 특성이 있는 [JsonPolymorphic] 구체적인 클래스에는 필드가 discriminator 없습니다. OpenAPI를 사용하려면 판별자 속성이 스키마의 필수 속성이어야 하지만 판별자 속성이 구체적인 기본 클래스에 정의되어 있지 않으므로 스키마에 필드를 포함 discriminator 할 수 없습니다.

스키마 변환기를 사용하여 메타데이터 추가

스키마 변환기를 사용하여 모든 기본 메타데이터를 재정의하거나 값과 같은 example 추가 메타데이터를 생성된 스키마에 추가할 수 있습니다. 자세한 내용은 스키마 변환기 사용을 참조 하세요 .

OpenAPI 문서 생성을 사용자 지정하는 옵션

다음 섹션에서는 OpenAPI 문서 생성을 사용자 지정하는 방법을 보여 줍니다.

OpenAPI 문서 이름 사용자 지정

앱의 각 OpenAPI 문서에는 고유한 이름이 있습니다. 등록된 기본 문서 이름은 .입니다 v1.

builder.Services.AddOpenApi(); // Document name is v1

호출에 매개 변수로 이름을 전달하여 문서 이름을 수정할 AddOpenApi 수 있습니다.

builder.Services.AddOpenApi("internal"); // Document name is internal

문서 이름은 OpenAPI 구현의 여러 위치에 표시됩니다.

생성된 OpenAPI 문서를 가져올 때 문서 이름은 요청에서 documentName 매개 변수 인수로 제공됩니다. 다음 요청은 해당 및 internal 문서를 확인 v1 합니다.

GET http://localhost:5000/openapi/v1.json
GET http://localhost:5000/openapi/internal.json

생성된 문서의 OpenAPI 버전 사용자 지정

기본적으로 OpenAPI 문서 생성은 OpenAPI 사양의 v3.0을 준수하는 문서를 만듭니다. 다음 코드는 OpenAPI 문서의 기본 버전을 수정하는 방법을 보여 줍니다.

builder.Services.AddOpenApi(options =>
{
    options.OpenApiVersion = OpenApiSpecVersion.OpenApi2_0;
});

OpenAPI 엔드포인트 경로 사용자 지정

기본적으로 호출 MapOpenApi 을 통해 등록된 OpenAPI 엔드포인트는 엔드포인트에 /openapi/{documentName}.json 문서를 노출합니다. 다음 코드는 OpenAPI 문서가 등록된 경로를 사용자 지정하는 방법을 보여 줍니다.

app.MapOpenApi("/openapi/{documentName}/openapi.json");

엔드포인트 경로에서 경로 매개 변수를 documentName 제거할 수는 있지만 권장되지는 않습니다. 경로 매개 변수가 documentName 엔드포인트 경로에서 제거되면 프레임워크는 쿼리 매개 변수에서 문서 이름을 확인하려고 시도합니다. 경로 또는 쿼리에 제공하지 documentName 않으면 예기치 않은 동작이 발생할 수 있습니다.

OpenAPI 엔드포인트 사용자 지정

OpenAPI 문서는 경로 처리기 엔드포인트를 통해 제공되므로 표준 최소 엔드포인트에서 사용할 수 있는 모든 사용자 지정을 OpenAPI 엔드포인트에서 사용할 수 있습니다.

OpenAPI 문서 액세스를 권한 있는 사용자로 제한

OpenAPI 엔드포인트는 기본적으로 권한 부여 검사를 사용하도록 설정하지 않습니다. 그러나 권한 부여 검사는 OpenAPI 문서에 적용할 수 있습니다. 다음 코드에서 OpenAPI 문서에 대한 액세스는 역할이 있는 문서로 tester 제한됩니다.

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddAuthorization(o =>
{
    o.AddPolicy("ApiTesterPolicy", b => b.RequireRole("tester"));
});
builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi()
    .RequireAuthorization("ApiTesterPolicy");

app.MapGet("/", () => "Hello world!");

app.Run();

생성된 OpenAPI 문서 캐시

OpenAPI 엔드포인트에 대한 요청이 전송될 때마다 OpenAPI 문서가 다시 생성됩니다. 다시 생성을 통해 변환기는 동적 애플리케이션 상태를 작업에 통합할 수 있습니다. 예를 들어 HTTP 컨텍스트의 세부 정보를 사용하여 요청을 다시 생성합니다. 해당하는 경우 각 HTTP 요청에서 문서 생성 파이프라인을 실행하지 않도록 OpenAPI 문서를 캐시할 수 있습니다.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(policy => policy.Expire(TimeSpan.FromMinutes(10)));
});
builder.Services.AddOpenApi();

var app = builder.Build();

app.UseOutputCache();

app.MapOpenApi()
    .CacheOutput();

app.MapGet("/", () => "Hello world!");

app.Run();

OpenAPI 문서 변환기

이 섹션에서는 변환기를 사용하여 OpenAPI 문서를 사용자 지정하는 방법을 보여 줍니다.

변환기를 사용하여 OpenAPI 문서 사용자 지정

변환기는 사용자 정의 사용자 지정을 사용하여 OpenAPI 문서를 수정하기 위한 API를 제공합니다. 변환기는 다음과 같은 시나리오에 유용합니다.

  • 문서의 모든 작업에 매개 변수를 추가합니다.
  • 매개 변수 또는 작업에 대한 설명 수정
  • OpenAPI 문서에 최상위 정보 추가

변환기는 다음 세 가지 범주로 분류됩니다.

  • 문서 변환기는 전체 OpenAPI 문서에 액세스할 수 있습니다. 문서를 전역으로 수정하는 데 사용할 수 있습니다.
  • 작업 변환기는 각 개별 작업에 적용됩니다. 각 개별 작업은 경로와 HTTP 메서드의 조합입니다. 엔드포인트에서 매개 변수 또는 응답을 수정하는 데 사용할 수 있습니다.
  • 스키마 변환기는 문서의 각 스키마에 적용됩니다. 요청 또는 응답 본문 또는 중첩된 스키마의 스키마를 수정하는 데 사용할 수 있습니다.

변환기는 개체의 메서드 OpenApiOptions 를 호출 AddDocumentTransformer 하여 문서에 등록할 수 있습니다. 다음 코드 조각은 변환기를 문서에 등록하는 다양한 방법을 보여줍니다.

using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.AddDocumentTransformer((document, context, cancellationToken)
                             => Task.CompletedTask);
    options.AddDocumentTransformer(new MyDocumentTransformer());
    options.AddDocumentTransformer<MyDocumentTransformer>();
    options.AddOperationTransformer((operation, context, cancellationToken)
                            => Task.CompletedTask);
    options.AddOperationTransformer(new MyOperationTransformer());
    options.AddOperationTransformer<MyOperationTransformer>();
    options.AddSchemaTransformer((schema, context, cancellationToken)
                            => Task.CompletedTask);
    options.AddSchemaTransformer(new MySchemaTransformer());
    options.AddSchemaTransformer<MySchemaTransformer>();
});

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => "Hello world!");

app.Run();

변환기에 대한 실행 순서

변환기는 등록에 따라 선입선 실행 순서로 실행됩니다. 다음 코드 조각에서 문서 변환기는 작업 변환기에서 수정한 내용에 액세스할 수 있습니다.

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.AddOperationTransformer((operation, context, cancellationToken)
                                     => Task.CompletedTask);
    options.AddDocumentTransformer((document, context, cancellationToken)
                                     => Task.CompletedTask);
});

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => "Hello world!");

app.Run();

문서 변환기 사용

문서 변환기는 다음을 포함하는 컨텍스트 개체에 액세스할 수 있습니다.

문서 변환기는 생성된 OpenAPI 문서를 변경할 수도 있습니다. 다음 예제에서는 OpenAPI 문서에 API에 대한 일부 정보를 추가하는 문서 변환기를 보여 줍니다.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.AddDocumentTransformer((document, context, cancellationToken) =>
    {
        document.Info = new()
        {
            Title = "Checkout API",
            Version = "v1",
            Description = "API for processing checkouts from cart."
        };
        return Task.CompletedTask;
    });
});

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => "Hello world!");

app.Run();

서비스 활성화 문서 변환기는 DI의 인스턴스를 활용하여 앱을 수정할 수 있습니다. 다음 샘플에서는 인증 계층의 서비스를 사용하는 IAuthenticationSchemeProvider 문서 변환기를 보여 줍니다. 앱에 JWT 전달자 관련 스키마가 등록되어 있는지 확인하고 OpenAPI 문서의 최상위 수준에 추가합니다.

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddAuthentication().AddJwtBearer();

builder.Services.AddOpenApi(options =>
{
    options.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => "Hello world!");

app.Run();

internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer
{
    public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
    {
        var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
        if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer"))
        {
            var requirements = new Dictionary<string, OpenApiSecurityScheme>
            {
                ["Bearer"] = new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.Http,
                    Scheme = "bearer", // "bearer" refers to the header name here
                    In = ParameterLocation.Header,
                    BearerFormat = "Json Web Token"
                }
            };
            document.Components ??= new OpenApiComponents();
            document.Components.SecuritySchemes = requirements;
        }
    }
}

문서 변환기는 연결된 문서 인스턴스에 고유합니다. 다음 예제에서는 변환기를 실행합니다.

  • 인증 관련 요구 사항을 문서에 등록합니다 internal .
  • public 문서를 수정되지 않은 상태로 둡니다.
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddAuthentication().AddJwtBearer();

builder.Services.AddOpenApi("internal", options =>
{
    options.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});
builder.Services.AddOpenApi("public");

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/world", () => "Hello world!")
    .WithGroupName("internal");
app.MapGet("/", () => "Hello universe!")
    .WithGroupName("public");

app.Run();

internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer
{
    public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
    {
        var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
        if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer"))
        {
            // Add the security scheme at the document level
            var requirements = new Dictionary<string, OpenApiSecurityScheme>
            {
                ["Bearer"] = new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.Http,
                    Scheme = "bearer", // "bearer" refers to the header name here
                    In = ParameterLocation.Header,
                    BearerFormat = "Json Web Token"
                }
            };
            document.Components ??= new OpenApiComponents();
            document.Components.SecuritySchemes = requirements;

            // Apply it as a requirement for all operations
            foreach (var operation in document.Paths.Values.SelectMany(path => path.Operations))
            {
                operation.Value.Security.Add(new OpenApiSecurityRequirement
                {
                    [new OpenApiSecurityScheme { Reference = new OpenApiReference { Id = "Bearer", Type = ReferenceType.SecurityScheme } }] = Array.Empty<string>()
                });
            }
        }
    }
}

작업 변환기 사용

작업은 OpenAPI 문서에서 HTTP 경로 및 메서드의 고유한 조합입니다. 작업 변환기는 다음과 같이 수정할 때 유용합니다.

  • 앱의 각 엔드포인트에 대해 만들어야 합니다. 또는
  • 특정 경로에 조건부로 적용됩니다.

작업 변환기는 다음을 포함하는 컨텍스트 개체에 액세스할 수 있습니다.

  • 작업이 속한 문서의 이름입니다.
  • ApiDescription 작업과 연결된 것입니다.
  • IServiceProvider 문서 생성에 사용되는 것입니다.

예를 들어 다음 작업 변환기는 문서의 모든 작업에서 지원하는 응답 상태 코드로 추가 500 합니다.

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddAuthentication().AddJwtBearer();

builder.Services.AddOpenApi(options =>
{
    options.AddOperationTransformer((operation, context, cancellationToken) =>
    {
        operation.Responses.Add("500", new OpenApiResponse { Description = "Internal server error" });
        return Task.CompletedTask;
    });
});

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => "Hello world!");

app.Run();

스키마 변환기 사용

스키마는 OpenAPI 문서의 요청 및 응답 본문에 사용되는 데이터 모델입니다. 스키마 변환기는 다음과 같이 수정할 때 유용합니다.

  • 문서의 각 스키마에 대해 만들어야 합니다. 또는
  • 특정 스키마에 조건부로 적용됩니다.

스키마 변환기는 다음을 포함하는 컨텍스트 개체에 액세스할 수 있습니다.

  • 스키마가 속한 문서의 이름입니다.
  • 대상 스키마와 연결된 JSON 형식 정보입니다.
  • IServiceProvider 문서 생성에 사용되는 것입니다.

예를 들어 다음 스키마 변환기는 소수점 형식 decimalformat 다음 대신 double설정합니다.

using Microsoft.AspNetCore.OpenApi;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options => {
    // Schema transformer to set the format of decimal to 'decimal'
    options.AddSchemaTransformer((schema, context, cancellationToken) =>
    {
        if (context.JsonTypeInfo.Type == typeof(decimal))
        {
            schema.Format = "decimal";
        }
        return Task.CompletedTask;
    });
});

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => new Body { Amount = 1.1m });

app.Run();

public class Body {
    public decimal Amount { get; set; }
}

추가 리소스

최소 API는 Microsoft.AspNetCore.OpenApi 패키지를 통해 앱의 엔드포인트에 대한 정보를 생성하기 위한 기본 제공 지원을 제공합니다. 시각적 UI를 통해 생성된 OpenAPI 정의를 노출하려면 타사 패키지가 필요합니다. 컨트롤러 기반 API에서 OpenAPI 지원에 대한 자세한 내용은 이 문서의 .NET 9 버전을 참조 하세요.

다음 코드는 ASP.NET Core 최소 웹 API 템플릿에서 생성되며 OpenAPI를 사용합니다.

using Microsoft.AspNetCore.OpenApi;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateTime.Now.AddDays(index),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();

app.Run();

internal record WeatherForecast(DateTime Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

앞의 강조 표시된 코드에서 다음을 수행합니다.

  • Microsoft.AspNetCore.OpenApi는 다음 섹션에 설명되어 있습니다.
  • AddEndpointsApiExplorer : API 탐색기를 사용하여 기본 주석이 있는 엔드포인트를 검색하고 설명하도록 앱을 구성합니다. WithOpenApi는 API 탐색기에서 생성된 일치하는 기본 주석을 Microsoft.AspNetCore.OpenApi 패키지에서 생성된 주석으로 재정의합니다.
  • UseSwaggerSwagger 미들웨어를 추가합니다.
  • 'UseSwaggerUI'를 사용하면 포함된 버전의 Swagger UI 도구를 사용할 수 있습니다.
  • WithName: 엔드포인트의 IEndpointNameMetadata는 링크 생성에 사용되며 지정된 엔드포인트의 OpenAPI 사양에서 작업 ID로 처리됩니다.
  • WithOpenApi는 이 문서 뒷부분에 설명되어 있습니다.

Microsoft.AspNetCore.OpenApi NuGet 패키지

ASP.NET Core는 엔드포인트에 대한 OpenAPI 사양과 상호 작용하는 Microsoft.AspNetCore.OpenApi 패키지를 제공합니다. 이 패키지는 Microsoft.AspNetCore.OpenApi 패키지에 정의된 OpenAPI 모델과 최소 API에 정의된 엔드포인트 간의 링크 역할을 합니다. 이 패키지는 엔드포인트의 매개 변수, 응답 및 메타데이터를 검사하여 엔드포인트를 설명하는 데 사용되는 OpenAPI 주석 형식을 생성하는 API를 제공합니다.

Microsoft.AspNetCore.OpenApi는 프로젝트 파일에 PackageReference로 추가됩니다.

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>    
    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.*-*" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
  </ItemGroup>

</Project>

Microsoft.AspNetCore.OpenApi, Swashbuckle.AspNetCore와 함께 Swashbuckle.AspNetCore를 사용할 때 6.4.0 이상을 사용해야 합니다. WithOpenApi 호출에서 복사 생성자를 활용하려면 Microsoft.OpenApi 1.4.3 이상을 사용해야 합니다.

WithOpenApi를 통해 엔드포인트에 OpenAPI 주석 추가

엔드포인트에서 WithOpenApi를 호출하면 엔드포인트의 메타데이터가 추가됩니다. 이 메타데이터는 다음과 같습니다.

  • Swashbuckle.AspNetCore와 같은 타사 패키지에서 사용합니다.
  • Swagger 사용자 인터페이스 또는 API를 정의하기 위해 생성된 YAML 또는 JSON에 표시됩니다.
app.MapPost("/todoitems/{id}", async (int id, Todo todo, TodoDb db) =>
{
    todo.Id = id;
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
})
.WithOpenApi();

WithOpenApi에서 OpenAPI 주석 수정

WithOpenApi 메서드는 OpenAPI 주석을 수정하는 데 사용할 수 있는 함수를 허용합니다. 예를 들어 다음 코드에서는 엔드포인트의 첫 번째 매개 변수에 설명이 추가됩니다.

app.MapPost("/todo2/{id}", async (int id, Todo todo, TodoDb db) =>
{
    todo.Id = id;
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
})
.WithOpenApi(generatedOperation =>
{
    var parameter = generatedOperation.Parameters[0];
    parameter.Description = "The ID associated with the created Todo";
    return generatedOperation;
});

OpenAPI에 작업 ID 추가

작업 ID는 OpenAPI에서 지정된 엔드포인트를 고유하게 식별하는 데 사용됩니다. WithName 확장 메서드를 사용하여 메서드에 사용되는 작업 ID를 설정할 수 있습니다.

app.MapGet("/todoitems2", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithName("GetToDoItems");

또는 OpenAPI 주석에서 직접 OperationId 속성을 설정할 수 있습니다.

app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
    .WithOpenApi(operation => new(operation)
    {
        OperationId = "GetTodos"
    });

OpenAPI 설명에 태그 추가

OpenAPI는 태그 개체 를 사용하여 작업을 분류할 수 있도록 지원합니다. 이러한 태그는 일반적으로 Swagger UI에서 작업을 그룹화하는 데 사용됩니다. 이러한 태그는 원하는 태그를 사용하여 엔드포인트에서 WithTags 확장 메서드를 호출하여 작업에 추가할 수 있습니다.

app.MapGet("/todoitems", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithTags("TodoGroup");

또는 WithOpenApi 확장 메서드를 통해 OpenAPI 주석에서 OpenApiTags 목록을 설정할 수 있습니다.

app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
    .WithOpenApi(operation => new(operation)
    {
        Tags = new List<OpenApiTag> { new() { Name = "Todos" } }
    });

엔드포인트 요약 또는 설명 추가

WithOpenApi 확장 메서드를 호출하여 엔드포인트 요약 및 설명을 추가할 수 있습니다. 다음 코드에서 요약은 OpenAPI 주석에 직접 설정됩니다.

app.MapGet("/todoitems2", async (TodoDb db) => await db.Todos.ToListAsync())
    .WithOpenApi(operation => new(operation)
    {
        Summary = "This is a summary",
        Description = "This is a description"
    });

OpenAPI 설명 제외

다음 샘플에서 /skipme 엔드포인트는 OpenAPI 설명 생성에서 제외됩니다.

using Microsoft.AspNetCore.OpenApi;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapGet("/swag", () => "Hello Swagger!")
    .WithOpenApi();
app.MapGet("/skipme", () => "Skipping Swagger.")
                    .ExcludeFromDescription();

app.Run();

API를 사용되지 않는 것으로 표시

엔드포인트를 사용되지 않는 것으로 표시하려면 OpenAPI 주석에서 Deprecated 속성을 설정합니다.

app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
    .WithOpenApi(operation => new(operation)
    {
        Deprecated = true
    });

응답 형식 설명

OpenAPI는 API에서 반환된 응답에 대한 설명을 제공할 수 있습니다. 최소 API는 엔드포인트의 응답 유형을 설정하기 위한 세 가지 전략을 지원합니다.

Produces 확장 메서드를 사용하여 엔드포인트에 Produces 메타데이터를 추가할 수 있습니다. 매개 변수가 제공되지 않으면 확장 메서드는 200 상태 코드 및 application/json 콘텐츠 형식에서 대상 형식에 대한 메타데이터를 채웁니다.

app
    .MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
    .Produces<IList<Todo>>();

엔드포인트의 경로 처리기 구현에서 TypedResults를 사용하면 엔드포인트에 대한 응답 형식 메타데이터가 자동으로 포함됩니다. 예를 들어, 다음 코드는 application/json 콘텐츠 형식의 200 상태 코드 아래에 응답으로 엔드포인트에 자동으로 주석을 추가합니다.

app.MapGet("/todos", async (TodoDb db) =>
{
    var todos = await db.Todos.ToListAsync());
    return TypedResults.Ok(todos);
});

ProblemDetails에 대한 응답 설정

ProblemDetails 응답, ProducesProblem 확장 메서드 ProducesValidationProblem를 반환하거나 TypedResults.Problem 엔드포인트의 메타데이터에 적절한 주석을 추가하는 데 사용할 수 있는 엔드포인트에 대한 응답 유형을 설정하는 경우 ProducesProblem.NET 8 이하의 경로 그룹에ProducesValidationProblem 확장 메서드와 확장 메서드를 사용할 수 없습니다.

위의 전략 중 하나에서 제공하는 명시적 주석이 없는 경우, 프레임워크는 응답의 서명을 검사하여 기본 응답 유형을 확인하려고 시도합니다. 이 기본 응답은 OpenAPI 정의의 200 상태 코드 아래에 채워집니다.

여러 응답 유형

엔드포인트가 다양한 시나리오에서 다른 응답 형식을 반환할 수 있는 경우 다음과 같은 방법으로 메타데이터를 제공할 수 있습니다.

  • 다음 예제와 같이 Produces 확장 메서드를 여러 번 호출합니다.

    app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) =>
             await db.Todos.FindAsync(id) 
             is Todo todo
             ? Results.Ok(todo) 
             : Results.NotFound())
       .Produces<Todo>(StatusCodes.Status200OK)
       .Produces(StatusCodes.Status404NotFound);
    
  • 다음 예제와 같이, 서명에 Results<TResult1,TResult2,TResultN>을 사용하고 처리기 본문에 TypedResults를 사용합니다.

    app.MapGet("/book/{id}", Results<Ok<Book>, NotFound> (int id, List<Book> bookList) =>
    {
        return bookList.FirstOrDefault((i) => i.Id == id) is Book book
         ? TypedResults.Ok(book)
         : TypedResults.NotFound();
    });
    

    공용 구조체 형식Results<TResult1,TResult2,TResultN> 경로 처리기가 구현하는 구체적인 형식을 여러 IResult번 반환하고, 구현 IEndpointMetadataProvider 하는 형식 중 하나라도 엔드포인트의 메타데이터에 기여한다고 선언합니다.

    공용 구조체 형식은 암시적 캐스트 연산자를 구현합니다. 이러한 연산자를 사용하면 컴파일러가 제네릭 인수에 지정된 형식을 공용 구조체 형식의 인스턴스로 자동으로 변환할 수 있습니다. 이 기능은 경로 처리기가 선언한 결과만 반환한다는 컴파일 타임 검사를 제공하는 추가 이점이 있습니다. 제네릭 인수 중 하나로 선언되지 않은 형식을 Results<TResult1,TResult2,TResultN>로 반환하려고 시도하면 컴파일 오류가 발생합니다.

요청 본문 및 매개 변수 설명

OpenAPI는 엔드포인트에서 반환되는 형식을 설명하는 것 외에도 API에서 사용하는 입력에 주석을 추가하는 것도 지원합니다. 이러한 입력은 다음 두 가지 범주로 분류됩니다.

  • 경로, 쿼리 문자열, 헤더 또는 쿠키에 표시되는 매개 변수
  • 요청 본문의 일부로 전송되는 데이터

프레임워크는 경로 처리기의 서명에 따라 경로, 쿼리 및 헤더 문자열의 요청 매개 변수 형식을 자동으로 유추합니다.

요청 본문으로 전송되는 입력 유형을 정의하려면 Accepts 확장 메서드를 사용하여 요청 처리기에서 예상하는 개체 형식 및 콘텐츠 형식을 정의하여 속성을 구성합니다. 다음 예제에서 엔드포인트는 요청 본문에 예상되는 콘텐츠 형식이 application/xmlTodo 개체를 허용합니다.

app.MapPost("/todos/{id}", (int id, Todo todo) => ...)
  .Accepts<Todo>("application/xml");

Accepts 확장 메서드 외에도 매개 변수 형식은 IEndpointParameterMetadataProvider 인터페이스를 구현하여 자체 주석을 설명할 수 있습니다. 예를 들어, 다음 Todo 형식은 application/xml 콘텐츠 형식의 요청 본문이 필요한 주석을 추가합니다.

public class Todo : IEndpointParameterMetadataProvider
{
    public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
    {
        builder.Metadata.Add(new ConsumesAttribute(typeof(Todo), isOptional: false, "application/xml"));
    }
}

명시적 주석이 제공되지 않으면 프레임워크는 엔드포인트 처리기에 요청 본문 매개 변수가 있는 경우 기본 요청 유형을 확인하려고 시도합니다. 유추는 다음 추론을 사용하여 주석을 생성합니다.

  • [FromForm] 특성을 통해 양식에서 읽은 요청 본문 매개 변수는 multipart/form-data 콘텐츠 형식으로 설명됩니다.
  • 다른 모든 요청 본문 매개 변수는 application/json 콘텐츠 형식으로 설명됩니다.
  • 요청 본문은 nullable이거나 AllowEmpty 속성이 FromBody 특성에 설정된 경우 선택 사항으로 처리됩니다.

API 버전 관리 지원

최소 API는 Asp.Versioning.Http 패키지를 통해 API 버전 관리를 지원합니다. 최소 API를 사용하여 버전 관리를 구성하는 예제는 API 버전 관리 리포지토리에서 찾을 수 있습니다.

GitHub의 ASP.NET Core OpenAPI 소스 코드

추가 리소스

최소 API 앱은 Swashbuckle을 사용하는 경로 처리기에 대한 OpenAPI 사양을 설명할 수 있습니다.

컨트롤러 기반 API에서 OpenAPI 지원에 대한 자세한 내용은 이 문서의 .NET 9 버전을 참조 하세요.

다음 코드는 OpenAPI를 지원하는 일반적인 ASP.NET Core 앱입니다.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new() { Title = builder.Environment.ApplicationName,
                               Version = "v1" });
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger(); // UseSwaggerUI Protected by if (env.IsDevelopment())
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json",
                                    $"{builder.Environment.ApplicationName} v1"));
}

app.MapGet("/swag", () => "Hello Swagger!");

app.Run();

OpenAPI 설명 제외

다음 샘플에서 /skipme 엔드포인트는 OpenAPI 설명 생성에서 제외됩니다.

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(); // UseSwaggerUI Protected by if (env.IsDevelopment())
}

app.MapGet("/swag", () => "Hello Swagger!");
app.MapGet("/skipme", () => "Skipping Swagger.")
                    .ExcludeFromDescription();

app.Run();

응답 형식 설명

다음 예는 기본 제공 결과 형식을 사용하여 응답을 사용자 지정합니다.

app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) =>
         await db.Todos.FindAsync(id) 
         is Todo todo
         ? Results.Ok(todo) 
         : Results.NotFound())
   .Produces<Todo>(StatusCodes.Status200OK)
   .Produces(StatusCodes.Status404NotFound);

OpenAPI에 작업 ID 추가

app.MapGet("/todoitems2", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithName("GetToDoItems");

OpenAPI 설명에 태그 추가

다음 코드는 OpenAPI 그룹화 태그를 사용합니다.

app.MapGet("/todoitems", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithTags("TodoGroup");