Bagikan melalui


Bekerja dengan dokumen OpenAPI

Paket ini Microsoft.AspNetCore.OpenApi menyediakan dukungan bawaan untuk pembuatan dokumen OpenAPI di ASP.NET Core. Paket ini menyediakan fitur-fitur berikut:

  • Dukungan untuk menghasilkan dokumen OpenAPI pada waktu proses dan mengaksesnya melalui titik akhir pada aplikasi.
  • Dukungan untuk API "transformer" yang memungkinkan modifikasi dokumen yang dihasilkan.
  • Dukungan untuk menghasilkan beberapa dokumen OpenAPI dari satu aplikasi.
  • Memanfaatkan JSdukungan skema ON yang disediakan oleh System.Text.Json.
  • Kompatibel dengan AoT asli.

Penginstalan paket

Pasang paket Microsoft.AspNetCore.OpenApi:

Jalankan perintah berikut dari Konsol Manajer Paket:

Install-Package Microsoft.AspNetCore.OpenApi -IncludePrerelease

Untuk menambahkan dukungan untuk membuat dokumen OpenAPI pada waktu build, instal Microsoft.Extensions.ApiDescription.Server paket:

Jalankan perintah berikut dari Konsol Manajer Paket:

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

Mengonfigurasi pembuatan dokumen OpenAPI

Kode berikut:

  • Menambahkan layanan OpenAPI.
  • Mengaktifkan titik akhir untuk menampilkan dokumen OpenAPI dalam JSformat ON.
var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi();

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

app.Run();

Luncurkan aplikasi dan navigasi ke https://localhost:<port>/openapi/v1.json untuk melihat dokumen OpenAPI yang dihasilkan.

Menyertakan metadata OpenAPI dalam aplikasi web ASP.NET

ASP.NET mengumpulkan metadata dari titik akhir aplikasi web dan menggunakannya untuk menghasilkan dokumen OpenAPI. Dalam aplikasi berbasis pengontrol, metadata dikumpulkan dari atribut seperti [EndpointDescription], , [HttpPost]dan [Produces]. Dalam API minimal, metadata dapat dikumpulkan dari atribut, tetapi juga dapat diatur dengan menggunakan metode ekstensi dan strategi lain, seperti kembali TypedResults dari penangan rute. Tabel berikut ini menyediakan gambaran umum metadata yang dikumpulkan dan strategi untuk mengaturnya.

Metadata Atribut Metode ekstensi Strategi lain
ringkasan [EndpointSummary] WithSummary
description [EndpointDescription] WithDescription
tag [Tags] WithTags
operationId [EndpointName] WithName
parameter [FromQuery], , [FromRoute][FromHeader],[FromForm]
deskripsi parameter [Description]
requestBody [FromBody] Accepts
Responses to [Produces], [ProducesProblem] Produces, ProducesProblem TypedResults
Mengecualikan titik akhir [ExcludeFromDescription] ExcludeFromDescription

ASP.NET Core tidak mengumpulkan metadata dari komentar dokumen XML.

Bagian berikut menunjukkan cara menyertakan metadata dalam aplikasi untuk menyesuaikan dokumen OpenAPI yang dihasilkan.

Ringkasan dan deskripsi

Ringkasan dan deskripsi titik akhir dapat diatur menggunakan [EndpointSummary] atribut dan [EndpointDescription] , atau dalam API minimal, menggunakan WithSummary metode ekstensi dan WithDescription .

Sampel berikut menunjukkan berbagai strategi untuk mengatur ringkasan dan deskripsi.

Perhatikan bahwa atribut ditempatkan pada metode delegasi dan bukan pada aplikasi. Metode 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!");

tag

OpenAPI mendukung penentuan tag pada setiap titik akhir sebagai bentuk kategorisasi. Dalam aplikasi berbasis pengontrol, nama pengontrol secara otomatis ditambahkan sebagai tag pada setiap titik akhirnya, tetapi ini dapat ditimpa menggunakan [Tags] atribut . Dalam API minimal, tag dapat diatur menggunakan [Tags] atribut atau WithTags metode ekstensi.

Sampel berikut menunjukkan berbagai strategi untuk mengatur tag.

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

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

operationId

OpenAPI mendukung operationId pada setiap titik akhir sebagai pengidentifikasi atau nama unik untuk operasi. Di aplikasi berbasis pengontrol, operationId dapat diatur menggunakan [EndpointName] atribut . Dalam API minimal, operationId dapat diatur menggunakan [EndpointName] atribut atau WithName metode ekstensi.

Sampel berikut menunjukkan berbagai strategi untuk mengatur operationId.

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

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

parameter

OpenAPI mendukung jalur anotasi, string kueri, header, dan cookie parameter yang digunakan oleh API.

Kerangka kerja menyimpulkan jenis untuk parameter permintaan secara otomatis berdasarkan tanda tangan handler rute.

Atribut [Description] dapat digunakan untuk memberikan deskripsi untuk parameter.

Sampel berikut menunjukkan cara mengatur deskripsi untuk parameter.

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

requestBody

Untuk menentukan jenis input yang dikirimkan sebagai isi permintaan, konfigurasikan properti dengan menggunakan Accepts metode ekstensi untuk menentukan jenis objek dan jenis konten yang diharapkan oleh penangan permintaan. Dalam contoh berikut, titik akhir menerima Todo objek dalam isi permintaan dengan tipe konten yang diharapkan dari application/xml.

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

Selain Accepts metode ekstensi, jenis parameter dapat menggambarkan anotasinya sendiri dengan mengimplementasikan IEndpointParameterMetadataProvider antarmuka. Misalnya, jenis berikut Todo menambahkan anotasi yang memerlukan isi permintaan dengan application/xml jenis konten.

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

Ketika tidak ada anotasi eksplisit yang disediakan, kerangka kerja mencoba menentukan jenis permintaan default jika ada parameter isi permintaan di handler titik akhir. Inferensi menggunakan heuristik berikut untuk menghasilkan anotasi:

  • Parameter isi permintaan yang dibaca dari formulir melalui [FromForm] atribut dijelaskan dengan multipart/form-data jenis konten.
  • Semua parameter isi permintaan lainnya dijelaskan dengan application/json jenis konten.
  • Isi permintaan diperlakukan sebagai opsional jika dapat diubah ke null atau jika AllowEmpty properti diatur pada FromBody atribut .

Menjelaskan jenis respons

OpenAPI mendukung memberikan deskripsi respons yang dikembalikan dari API. API minimal mendukung tiga strategi untuk mengatur jenis respons titik akhir:

Metode Produces ekstensi dapat digunakan untuk menambahkan Produces metadata ke titik akhir. Ketika tidak ada parameter yang disediakan, metode ekstensi mengisi metadata untuk jenis yang ditargetkan di bawah 200 kode status dan application/json jenis konten.

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

Menggunakan TypedResults dalam implementasi handler rute titik akhir secara otomatis menyertakan metadata jenis respons untuk titik akhir. Misalnya, kode berikut secara otomatis membuat anotasi titik akhir dengan respons di bawah 200 kode status dengan application/json jenis konten.

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

Mengatur respons untuk ProblemDetails

Saat mengatur jenis respons untuk titik akhir yang dapat mengembalikan respons ProblemDetails, ProducesProblem metode ekstensi atau TypedResults.Problem dapat digunakan untuk menambahkan anotasi yang sesuai ke metadata titik akhir.

Ketika tidak ada anotasi eksplisit yang disediakan oleh salah satu strategi ini, kerangka kerja mencoba menentukan jenis respons default dengan memeriksa tanda tangan respons. Respons default ini diisi di bawah 200 kode status dalam definisi OpenAPI.

Beberapa jenis respons

Jika titik akhir dapat mengembalikan jenis respons yang berbeda dalam skenario yang berbeda, Anda dapat menyediakan metadata dengan cara berikut:

  • Produces Panggil metode ekstensi beberapa kali, seperti yang ditunjukkan dalam contoh berikut:

    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);
    
  • Gunakan Results<TResult1,TResult2,TResultN> dalam tanda tangan dan TypedResults di isi handler, seperti yang ditunjukkan dalam contoh berikut:

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

    Jenis Results<TResult1,TResult2,TResultN> gabungan menyatakan bahwa handler rute mengembalikan beberapa IResultjenis beton yang menerapkan, dan salah satu jenis yang menerapkan IEndpointMetadataProvider akan berkontribusi pada metadata titik akhir.

    Jenis serikat terapkan operator cast implisit. Operator ini memungkinkan pengkompilasi untuk secara otomatis mengonversi jenis yang ditentukan dalam argumen generik ke instans jenis gabungan. Kemampuan ini memiliki manfaat tambahan untuk memberikan pemeriksaan waktu kompilasi bahwa handler rute hanya mengembalikan hasil yang dinyatakannya. Mencoba mengembalikan jenis yang tidak dinyatakan sebagai salah satu argumen generik untuk Results<TResult1,TResult2,TResultN> menghasilkan kesalahan kompilasi.

Mengecualikan titik akhir dari dokumen yang dihasilkan

Secara default, semua titik akhir yang ditentukan dalam aplikasi didokumenkan dalam file OpenAPI yang dihasilkan. API minimal mendukung dua strategi untuk mengecualikan titik akhir tertentu dari dokumen OpenAPI, menggunakan:

Sampel berikut menunjukkan berbagai strategi untuk mengecualikan titik akhir tertentu dari dokumen OpenAPI yang dihasilkan.

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

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

Opsi untuk Menyesuaikan pembuatan dokumen OpenAPI

Bagian berikut menunjukkan cara menyesuaikan pembuatan dokumen OpenAPI.

Mengkustomisasi nama dokumen OpenAPI

Setiap dokumen OpenAPI dalam aplikasi memiliki nama yang unik. Nama dokumen default yang terdaftar adalah v1.

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

Nama dokumen dapat dimodifikasi dengan meneruskan nama sebagai parameter ke AddOpenApi panggilan.

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

Nama dokumen muncul di beberapa tempat dalam implementasi OpenAPI.

Saat mengambil dokumen OpenAPI yang dihasilkan, nama dokumen disediakan sebagai documentName argumen parameter dalam permintaan. Permintaan berikut menyelesaikan v1 dokumen dan internal .

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

Mengkustomisasi versi OpenAPI dari dokumen yang dihasilkan

Secara default, pembuatan dokumen OpenAPI membuat dokumen yang sesuai dengan v3.0 dari spesifikasi OpenAPI. Kode berikut menunjukkan cara mengubah versi default dokumen OpenAPI:

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

Mengkustomisasi rute titik akhir OpenAPI

Secara default, titik akhir OpenAPI terdaftar melalui panggilan untuk MapOpenApi mengekspos dokumen di /openapi/{documentName}.json titik akhir. Kode berikut menunjukkan cara mengkustomisasi rute tempat dokumen OpenAPI terdaftar:

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

Dimungkinkan, tetapi tidak disarankan, untuk menghapus documentName parameter rute dari rute titik akhir. documentName Saat parameter rute dihapus dari rute titik akhir, kerangka kerja mencoba menyelesaikan nama dokumen dari parameter kueri. Tidak menyediakan documentName dalam rute atau kueri dapat mengakibatkan perilaku yang tidak terduga.

Mengkustomisasi titik akhir OpenAPI

Karena dokumen OpenAPI disajikan melalui titik akhir handler rute, kustomisasi apa pun yang tersedia untuk titik akhir minimal standar tersedia untuk titik akhir OpenAPI.

Membatasi akses dokumen OpenAPI ke pengguna yang berwenang

Titik akhir OpenAPI tidak mengaktifkan pemeriksaan otorisasi secara default. Namun, dimungkinkan untuk membatasi akses ke dokumen OpenAPI. Misalnya, dalam kode berikut, akses ke dokumen OpenAPI terbatas pada yang memiliki tester peran:

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

Dokumen OpenAPI yang dihasilkan cache

Dokumen OpenAPI diregenerasi setiap kali permintaan ke titik akhir OpenAPI dikirim. Regenerasi memungkinkan transformator untuk menggabungkan status aplikasi dinamis ke dalam operasi mereka. Misalnya, meregenerasi permintaan dengan detail konteks HTTP. Jika berlaku, dokumen OpenAPI dapat di-cache untuk menghindari eksekusi alur pembuatan dokumen pada setiap permintaan HTTP.

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

Transformator dokumen OpenAPI

Bagian ini menunjukkan cara menyesuaikan dokumen OpenAPI dengan transformator.

Menyesuaikan dokumen OpenAPI dengan transformator

Transformer menyediakan API untuk memodifikasi dokumen OpenAPI dengan kustomisasi yang ditentukan pengguna. Transformer berguna untuk skenario seperti:

  • Menambahkan parameter ke semua operasi dalam dokumen.
  • Memodifikasi deskripsi untuk parameter atau operasi.
  • Menambahkan informasi tingkat atas ke dokumen OpenAPI.

Transformer termasuk dalam dua kategori:

  • Transformator dokumen memiliki akses ke seluruh dokumen OpenAPI. Ini dapat digunakan untuk membuat modifikasi global pada dokumen.
  • Transformator operasi berlaku untuk setiap operasi individu. Setiap operasi individu adalah kombinasi jalur dan metode HTTP. Ini dapat digunakan untuk mengubah parameter atau respons pada titik akhir.

Transformer dapat didaftarkan ke dokumen melalui UseTransformer panggilan pada OpenApiOptions objek. Cuplikan berikut menunjukkan berbagai cara untuk mendaftarkan transformator ke dokumen:

  • Daftarkan transformator dokumen menggunakan delegasi.
  • Daftarkan transformator dokumen menggunakan instans IOpenApiDocumentTransformer.
  • Daftarkan transformator dokumen menggunakan yang diaktifkan IOpenApiDocumentTransformerDI .
  • Daftarkan transformator operasi menggunakan delegasi.
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.UseTransformer((document, context, cancellationToken) 
                             => Task.CompletedTask);
    options.UseTransformer(new MyDocumentTransformer());
    options.UseTransformer<MyDocumentTransformer>();
    options.UseOperationTransformer((operation, context, cancellationToken)
                            => Task.CompletedTask);
});

var app = builder.Build();

app.MapOpenApi();

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

app.Run();

Urutan eksekusi untuk transformator

Transformer dijalankan dalam urutan first-in first-out berdasarkan pendaftaran. Dalam cuplikan berikut, transformator dokumen memiliki akses ke modifikasi yang dibuat oleh transformator operasi:

var builder = WebApplication.CreateBuilder();

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

var app = builder.Build();

app.MapOpenApi();

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

app.Run();

Menggunakan transformator dokumen

Transformator dokumen memiliki akses ke objek konteks yang mencakup:

  • Nama dokumen yang sedang dimodifikasi.
  • Daftar yang ApiDescriptionGroups terkait dengan dokumen tersebut.
  • yang IServiceProvider digunakan dalam pembuatan dokumen.

Transformator dokumen juga dapat mengubah dokumen OpenAPI yang dihasilkan. Contoh berikut menunjukkan transformator dokumen yang menambahkan beberapa informasi tentang API ke dokumen OpenAPI.

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

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.UseTransformer((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();

Transformer dokumen yang diaktifkan layanan dapat menggunakan instans dari DI untuk memodifikasi aplikasi. Sampel berikut menunjukkan transformator dokumen yang menggunakan IAuthenticationSchemeProvider layanan dari lapisan autentikasi. Ini memeriksa apakah ada skema terkait pembawa JWT yang terdaftar di aplikasi dan menambahkannya ke tingkat atas dokumen 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.UseTransformer<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;
        }
    }
}

Transformator dokumen unik untuk instans dokumen yang terkait dengannya. Dalam contoh berikut, transformator:

  • Mendaftarkan persyaratan terkait autentikasi ke internal dokumen.
  • Membiarkan dokumen tidak dimodifikasi 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.UseTransformer<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>()
                });
            }
        }
    }
}

Menggunakan transformator operasi

Operasi adalah kombinasi unik jalur dan metode HTTP dalam dokumen OpenAPI. Transformer operasi sangat membantu ketika modifikasi:

  • Harus dibuat untuk setiap titik akhir dalam aplikasi, atau
  • Diterapkan secara kondisional ke rute tertentu.

Transformer operasi memiliki akses ke objek konteks yang berisi:

  • Nama dokumen tempat operasi berada.
  • Yang ApiDescription terkait dengan operasi.
  • yang IServiceProvider digunakan dalam pembuatan dokumen.

Misalnya, transformator operasi berikut menambahkan 500 sebagai kode status respons yang didukung oleh semua operasi dalam dokumen.

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

Menggunakan dokumen OpenAPI yang dihasilkan

Dokumen OpenAPI dapat dicolokkan ke ekosistem yang luas dari alat yang ada untuk pengujian, dokumentasi, dan pengembangan lokal.

Menggunakan UI Swagger untuk pengujian ad-hoc lokal

Secara default, Microsoft.AspNetCore.OpenApi paket tidak dikirim dengan dukungan bawaan untuk memvisualisasikan atau berinteraksi dengan dokumen OpenAPI. Alat populer untuk memvisualisasikan atau berinteraksi dengan dokumen OpenAPI termasuk UI Swagger dan ReDoc. UI Swagger dan ReDoc dapat diintegrasikan dalam aplikasi dalam beberapa cara. Editor seperti Visual Studio dan VS Code menawarkan ekstensi dan pengalaman bawaan untuk pengujian terhadap dokumen OpenAPI.

Paket ini Swashbuckle.AspNetCore.SwaggerUi menyediakan bundel aset web UI Swagger untuk digunakan dalam aplikasi. Paket ini dapat digunakan untuk merender UI untuk dokumen yang dihasilkan. Untuk mengonfigurasi ini, instal Swashbuckle.AspNetCore.SwaggerUi paket.

Aktifkan middleware swagger-ui dengan referensi ke rute OpenAPI yang terdaftar sebelumnya. Untuk membatasi pengungkapan informasi dan kerentanan keamanan, hanya aktifkan UI Swagger di lingkungan pengembangan.

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

var app = builder.Build();

app.MapOpenApi();
if (app.Environment.IsDevelopment())
{
    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/openapi/v1.json", "v1");
    });

}

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

app.Run();

Menggunakan Skalar untuk dokumentasi API interaktif

Skalar adalah UI dokumen interaktif sumber terbuka untuk OpenAPI. Skalar dapat diintegrasikan dengan titik akhir OpenAPI yang disediakan oleh ASP.NET Core. Untuk mengonfigurasi Skalar, instal Scalar.AspNetCore paket.

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

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi();

if (app.Environment.IsDevelopment())
{
    app.MapScalarApiReference();
}

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

app.Run();

Lint menghasilkan dokumen OpenAPI dengan Spectral

Spectral adalah linter dokumen OpenAPI sumber terbuka. Spectral dapat dimasukkan ke dalam build aplikasi untuk memverifikasi kualitas dokumen OpenAPI yang dihasilkan. Instal Spectral sesuai dengan petunjuk penginstalan paket.

Untuk memanfaatkan Spectral, instal Microsoft.Extensions.ApiDescription.Server paket untuk mengaktifkan pembuatan dokumen OpenAPI build-time.

Aktifkan pembuatan dokumen pada waktu build dengan mengatur properti berikut di file aplikasi .csproj Anda":

<PropertyGroup>
    <OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)</OpenApiDocumentsDirectory>
    <OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
</PropertyGroup>

Jalankan dotnet build untuk menghasilkan dokumen.

dotnet build

Buat .spectral.yml file dengan konten berikut.

extends: ["spectral:oas"]

Jalankan spectral lint pada file yang dihasilkan.

spectral lint WebMinOpenApi.json
...

The output shows any issues with the OpenAPI document.

```output
1:1  warning  oas3-api-servers       OpenAPI "servers" must be present and non-empty array.
3:10  warning  info-contact           Info object must have "contact" object.                        info
3:10  warning  info-description       Info "description" must be present and non-empty string.       info
9:13  warning  operation-description  Operation "description" must be present and non-empty string.  paths./.get
9:13  warning  operation-operationId  Operation must have "operationId".                             paths./.get

✖ 5 problems (0 errors, 5 warnings, 0 infos, 0 hints)

API minimal menyediakan dukungan bawaan untuk menghasilkan informasi tentang titik akhir dalam aplikasi melalui Microsoft.AspNetCore.OpenApi paket. Mengekspos definisi OpenAPI yang dihasilkan melalui UI visual memerlukan paket pihak ketiga. Untuk informasi tentang dukungan untuk OpenAPI di API berbasis pengontrol, lihat versi .NET 9 dari artikel ini.

Kode berikut dihasilkan oleh templat API web minimal ASP.NET Core dan menggunakan 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);
}

Dalam kode yang disorot sebelumnya:

  • Microsoft.AspNetCore.OpenApi dijelaskan di bagian berikutnya.
  • AddEndpointsApiExplorer : Mengonfigurasi aplikasi untuk menggunakan API Explorer untuk menemukan dan menjelaskan titik akhir dengan anotasi default. WithOpenApi mengambil alih pencocokan, anotasi default yang dihasilkan oleh API Explorer dengan anotasi yang dihasilkan dari Microsoft.AspNetCore.OpenApi paket.
  • UseSwaggermenambahkan middleware Swagger.
  • 'UseSwaggerUI' memungkinkan versi alat antarmuka pengguna Swagger yang disematkan.
  • WithNameIEndpointNameMetadata: Pada titik akhir digunakan untuk pembuatan tautan dan diperlakukan sebagai ID operasi dalam spesifikasi OpenAPI titik akhir yang diberikan.
  • WithOpenApi dijelaskan kemudian dalam artikel ini.

Microsoft.AspNetCore.OpenApi Paket NuGet

ASP.NET Core menyediakan Microsoft.AspNetCore.OpenApi paket untuk berinteraksi dengan spesifikasi OpenAPI untuk titik akhir. Paket bertindak sebagai tautan antara model OpenAPI yang ditentukan dalam Microsoft.AspNetCore.OpenApi paket dan titik akhir yang ditentukan dalam API Minimal. Paket ini menyediakan API yang memeriksa parameter, respons, dan metadata titik akhir untuk membuat jenis anotasi OpenAPI yang digunakan untuk menjelaskan titik akhir.

Microsoft.AspNetCore.OpenApi ditambahkan sebagai PackageReference ke file proyek:

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

Saat menggunakan Swashbuckle.AspNetCore dengan Microsoft.AspNetCore.OpenApi, Swashbuckle.AspNetCore 6.4.0 atau yang lebih baru harus digunakan. Microsoft.OpenApi 1.4.3 atau yang lebih baru harus digunakan untuk memanfaatkan konstruktor salinan dalam WithOpenApi pemanggilan.

Menambahkan anotasi OpenAPI ke titik akhir melalui WithOpenApi

WithOpenApi Panggilan pada titik akhir ditambahkan ke metadata titik akhir. Metadata ini dapat berupa:

  • Digunakan dalam paket pihak ketiga seperti Swashbuckle.AspNetCore.
  • Ditampilkan di antarmuka pengguna Swagger atau di YAML atau JSON yang dihasilkan untuk menentukan API.
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();

Mengubah anotasi OpenAPI di WithOpenApi

Metode WithOpenApi ini menerima fungsi yang dapat digunakan untuk memodifikasi anotasi OpenAPI. Misalnya, dalam kode berikut, deskripsi ditambahkan ke parameter pertama titik akhir:

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

Menambahkan ID operasi ke OpenAPI

ID operasi digunakan untuk mengidentifikasi titik akhir tertentu secara unik di OpenAPI. Metode WithName ekstensi dapat digunakan untuk mengatur ID operasi yang digunakan untuk metode .

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

Atau, OperationId properti dapat diatur langsung pada anotasi OpenAPI.

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

Menambahkan tag ke deskripsi OpenAPI

OpenAPI mendukung penggunaan objek tag untuk mengategorikan operasi. Tag ini biasanya digunakan untuk mengelompokkan operasi di antarmuka pengguna Swagger. Tag ini dapat ditambahkan ke operasi dengan memanggil metode ekstensi WithTags pada titik akhir dengan tag yang diinginkan.

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

Atau, daftar OpenApiTags dapat diatur pada anotasi OpenAPI melalui WithOpenApi metode ekstensi.

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

Menambahkan ringkasan atau deskripsi titik akhir

Ringkasan dan deskripsi titik akhir dapat ditambahkan dengan memanggil WithOpenApi metode ekstensi. Dalam kode berikut, ringkasan diatur langsung pada anotasi 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"
    });

Mengecualikan deskripsi OpenAPI

Dalam sampel berikut, /skipme titik akhir dikecualikan dari pembuatan deskripsi 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();

Menandai API sebagai usang

Untuk menandai titik akhir sebagai usang, atur Deprecated properti pada anotasi OpenAPI.

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

Menjelaskan jenis respons

OpenAPI mendukung memberikan deskripsi respons yang dikembalikan dari API. API minimal mendukung tiga strategi untuk mengatur jenis respons titik akhir:

Metode Produces ekstensi dapat digunakan untuk menambahkan Produces metadata ke titik akhir. Ketika tidak ada parameter yang disediakan, metode ekstensi mengisi metadata untuk jenis yang ditargetkan di bawah 200 kode status dan application/json jenis konten.

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

Menggunakan TypedResults dalam implementasi handler rute titik akhir secara otomatis menyertakan metadata jenis respons untuk titik akhir. Misalnya, kode berikut secara otomatis membuat anotasi titik akhir dengan respons di bawah 200 kode status dengan application/json jenis konten.

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

Mengatur respons untuk ProblemDetails

Saat mengatur jenis respons untuk titik akhir yang dapat mengembalikan respons ProblemDetails, ProducesProblem metode ekstensi atau TypedResults.Problem dapat digunakan untuk menambahkan anotasi yang sesuai ke metadata titik akhir.

Ketika tidak ada anotasi eksplisit yang disediakan oleh salah satu strategi di atas, kerangka kerja mencoba menentukan jenis respons default dengan memeriksa tanda tangan respons. Respons default ini diisi di bawah 200 kode status dalam definisi OpenAPI.

Beberapa jenis respons

Jika titik akhir dapat mengembalikan jenis respons yang berbeda dalam skenario yang berbeda, Anda dapat menyediakan metadata dengan cara berikut:

  • Produces Panggil metode ekstensi beberapa kali, seperti yang ditunjukkan dalam contoh berikut:

    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);
    
  • Gunakan Results<TResult1,TResult2,TResultN> dalam tanda tangan dan TypedResults di isi handler, seperti yang ditunjukkan dalam contoh berikut:

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

    Jenis Results<TResult1,TResult2,TResultN> gabungan menyatakan bahwa handler rute mengembalikan beberapa IResultjenis beton yang menerapkan, dan salah satu jenis yang menerapkan IEndpointMetadataProvider akan berkontribusi pada metadata titik akhir.

    Jenis serikat terapkan operator cast implisit. Operator ini memungkinkan pengkompilasi untuk secara otomatis mengonversi jenis yang ditentukan dalam argumen generik ke instans jenis gabungan. Kemampuan ini memiliki manfaat tambahan untuk memberikan pemeriksaan waktu kompilasi bahwa handler rute hanya mengembalikan hasil yang dinyatakannya. Mencoba mengembalikan jenis yang tidak dinyatakan sebagai salah satu argumen generik untuk Results<TResult1,TResult2,TResultN> menghasilkan kesalahan kompilasi.

Menjelaskan isi permintaan dan parameter

Selain menjelaskan jenis yang dikembalikan oleh titik akhir, OpenAPI juga mendukung anotasi input yang dikonsumsi oleh API. Input ini termasuk dalam dua kategori:

  • Parameter yang muncul di jalur, string kueri, header, atau cookies
  • Data yang dikirimkan sebagai bagian dari isi permintaan

Kerangka kerja menyimpulkan jenis untuk parameter permintaan di jalur, kueri, dan string header secara otomatis berdasarkan tanda tangan penangan rute.

Untuk menentukan jenis input yang dikirimkan sebagai isi permintaan, konfigurasikan properti dengan menggunakan Accepts metode ekstensi untuk menentukan jenis objek dan jenis konten yang diharapkan oleh penangan permintaan. Dalam contoh berikut, titik akhir menerima Todo objek dalam isi permintaan dengan tipe konten yang diharapkan dari application/xml.

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

Selain Accepts metode ekstensi, Jenis parameter dapat menggambarkan anotasinya sendiri dengan mengimplementasikan IEndpointParameterMetadataProvider antarmuka. Misalnya, jenis berikut Todo menambahkan anotasi yang memerlukan isi permintaan dengan application/xml jenis konten.

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

Ketika tidak ada anotasi eksplisit yang disediakan, kerangka kerja mencoba menentukan jenis permintaan default jika ada parameter isi permintaan di handler titik akhir. Inferensi menggunakan heuristik berikut untuk menghasilkan anotasi:

  • Parameter isi permintaan yang dibaca dari formulir melalui [FromForm] atribut dijelaskan dengan multipart/form-data jenis konten.
  • Semua parameter isi permintaan lainnya dijelaskan dengan application/json jenis konten.
  • Isi permintaan diperlakukan sebagai opsional jika dapat diubah ke null atau jika AllowEmpty properti diatur pada FromBody atribut .

Mendukung penerapan versi API

API minimal mendukung penerapan versi API melalui paket Asp.Versioning.Http. Contoh konfigurasi penerapan versi dengan API minimal dapat ditemukan di repositori penerapan versi API.

ASP.NET kode sumber Core OpenAPI di GitHub

Sumber Tambahan

Aplikasi API minimal dapat menjelaskan spesifikasi OpenAPI untuk handler rute menggunakan Swashbuckle.

Untuk informasi tentang dukungan untuk OpenAPI di API berbasis pengontrol, lihat versi .NET 9 dari artikel ini.

Kode berikut adalah aplikasi ASP.NET Core yang khas dengan dukungan OpenAPI:

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

Mengecualikan deskripsi OpenAPI

Dalam sampel berikut, /skipme titik akhir dikecualikan dari pembuatan deskripsi 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();

Menjelaskan jenis respons

Contoh berikut menggunakan jenis hasil bawaan untuk mengkustomisasi respons:

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

Menambahkan id operasi ke OpenAPI

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

Menambahkan tag ke deskripsi OpenAPI

Kode berikut menggunakan tag pengelompokan OpenAPI:

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