Aracılığıyla paylaş


Minimal API uygulamalarında yanıt oluşturma

Not

Bu, bu makalenin en son sürümü değildir. Geçerli sürüm için bu makalenin .NET 10 sürümüne bakın.

Uyarı

ASP.NET Core'un bu sürümü artık desteklenmiyor. Daha fazla bilgi için bkz . .NET ve .NET Core Destek İlkesi. Geçerli sürüm için bu makalenin .NET 9 sürümüne bakın.

Bu makalede, ASP.NET Core'da en düşük API uç noktaları için yanıtların nasıl oluşturulacağı açıklanmaktadır. Minimum API'ler, veri ve HTTP durum kodları döndürmek için çeşitli yollar sağlar.

En düşük uç noktalar aşağıdaki dönüş değeri türlerini destekler:

  1. string - Bu, Task<string> ve ValueTask<string>'yi içerir.
  2. T(Başka herhangi bir tür) - Bu, Task<T> ve ValueTask<T> içerir.
  3. IResult bazlı - Bu, Task<IResult> ve ValueTask<IResult> içerir.

Önemli

ASP.NET Core 10'dan başlayarak, bilinen API uç noktaları artık kimlik doğrulaması kullanılırken cookie oturum açma sayfalarına yönlendirilemeyecek. Bunun yerine, 401/403 durum kodlarını döndürür. Ayrıntılar için bkz. ASP.NET Core'da API uç noktası kimlik doğrulama davranışı.

string dönüş değerleri

Davranış İçerik Türü
Çerçeve, dizeyi doğrudan yanıta yazar. text/plain

Aşağıdaki, bir metin döndüren Hello world yol işleyicisini göz önünde bulundurun.

app.MapGet("/hello", () => "Hello World");

Durum 200 kodu, text/plain Content-Type üst bilgisi ve aşağıdaki içerikle döndürülür.

Hello World

T (Başka bir tür) değer döndürür

Davranış İçerik Türü
JSON çerçevesi yanıtı seri hale getirmektedir. application/json

Dize özelliği içeren Message anonim bir tür döndüren aşağıdaki yol işleyicisini göz önünde bulundurun.

app.MapGet("/hello", () => new { Message = "Hello World" });

Durum 200 kodu, application/json Content-Type üst bilgisi ve aşağıdaki içerikle döndürülür.

{"message":"Hello World"}

IResult dönüş değerleri

Davranış İçerik Türü
Çerçeve IResult.ExecuteAsync'i çağırır. Uygulama tarafından karar verilmiştir IResult .

Arabirimi, IResult HTTP uç noktasının sonucunu temsil eden bir sözleşme tanımlar. Statik Results sınıfı ve statik TypedResults , farklı yanıt türlerini temsil eden çeşitli IResult nesneler oluşturmak için kullanılır.

TypedResults ve Sonuçlar

Results ve TypedResults statik sınıfları benzer sonuç yardımcı kümeleri sağlar. TypedResults sınıfı, sınıfının Results eşdeğeridir. Ancak, Results yardımcılarının dönüş türü IResult iken, her TypedResults yardımcının dönüş türü, IResult uygulama türlerinden biridir. Fark, Results yardımcılar için somut tür gerektiğinde, örneğin birim testi için, bir dönüştürme yapılması gerektiği anlamına gelir. Uygulama türleri ad alanında Microsoft.AspNetCore.Http.HttpResults tanımlanır.

Döndürmek TypedResults yerine Results aşağıdaki avantajlara sahiptir:

  • TypedResults yardımcılar, katı bir şekilde türlendirilmiş nesneler döndürerek kod okunabilirliğini artırır, birim testlerini geliştirir ve çalışma zamanı hatalarının olasılığını azaltır.
  • Uygulama türü , uç noktayı açıklamak üzere OpenAPI için yanıt türü meta verilerini otomatik olarak sağlar.

Beklenen JSON yanıtına sahip bir 200 OK durum kodunun oluşturulduğu aşağıdaki uç noktayı göz önünde bulundurun.

app.MapGet("/hello", () => Results.Ok(new Message() { Text = "Hello World!" }))
    .Produces<Message>();

Bu uç noktayı doğru bir şekilde belgelendirebilmek için uzantılar yöntemi Produces çağrılır. Ancak, aşağıdaki kodda gösterildiği gibi Produces yerine TypedResults kullanılırsa, Results çağrısı yapılması gerekmez. TypedResults otomatik olarak uç nokta için meta verileri sağlar.

app.MapGet("/hello2", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));

Yanıt türünü açıklama hakkında daha fazla bilgi için bkz . Minimum API'lerde OpenAPI desteği.

Test sonucu türleriyle ilgili örnekler için Test belgelerine bakın.

Tüm Results üzerindeki yöntemler imzalarında IResult döndürdüğünden, derleyici, bir uç noktadan farklı sonuçlar dönerken bu durumu istek temsilcisi dönüş türü olarak otomatik bir şekilde çıkarsar. TypedResults, bu tür temsilcilerden Results<T1, TN> kullanımını gerektirir.

Aşağıdaki yöntem, dönülen nesnelerin gerçek somut türleri farklı olsa bile, hem Results.Ok hem de Results.NotFound döndüren IResult olarak bildirildiğinden derlenir.

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

Aşağıdaki yöntem derlenmez, çünkü TypedResults.Ok ve TypedResults.NotFound farklı türler döndüren olarak bildirilir ve derleyici en iyi eşleşen türü çıkarsamaya çalışmaz:

app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
     await db.Todos.FindAsync(id)
     is Todo todo
        ? TypedResults.Ok(todo)
        : TypedResults.NotFound());

TypedResults kullanmak için, dönüş türünün tam olarak bildirilmesi gerekir; yöntemin zaman uyumsuz olması durumunda, bildirimde dönüş türünün Task<> ile sarmalanması gerekir. Kullanmak TypedResults daha ayrıntılıdır, ancak tür bilgilerinin statik olarak kullanılabilir olması ve bu nedenle OpenAPI'ye kendi kendini açıklayabilme özelliğine sahip olması için bu bir avantajdır:

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

Sonuçlar<TResult1, TResultN>

Aşağıdaki durumlarda Results<TResult1, TResultN> yerine IResult uç nokta işleyicisi dönüş türü olarak kullanın:

  • Uç nokta işleyicisinden birden çok IResult uygulama türü döndürülür.
  • Statik TypedResult sınıfı, IResult nesnelerini yaratmak için kullanılır.

Bu alternatif, genel birleşim türleri otomatik olarak uç nokta meta verilerini koruduğundan döndürmekten IResult daha iyidir. Results<TResult1, TResultN> Birleşim türleri örtük atama işleçleri uyguladığından, derleyici genel bağımsız değişkenlerde belirtilen türleri otomatik olarak birleşim türünün bir örneğine dönüştürebilir.

Bu, bir yol işleyicisinin aslında yalnızca bildirdiği sonuçları döndürdüğüne ilişkin derleme zamanı denetimi sağlamanın ek avantajına sahiptir. Genel bağımsız değişkenlerden biri olarak bildirilmemiş bir tür döndürmeye Results<> çalışmak derleme hatasıyla sonuçlanır.

Aşağıdaki uç noktayı, 400 BadRequest, orderId değerinden büyük olduğunda 999 durum kodunun döndürüldüğü göz önünde bulundurun. Aksi takdirde, beklenen içeriğe sahip bir 200 OK oluşturur.

app.MapGet("/orders/{orderId}", IResult (int orderId)
    => orderId > 999 ? TypedResults.BadRequest() : TypedResults.Ok(new Order(orderId)))
    .Produces(400)
    .Produces<Order>();

Bu uç noktayı doğru bir şekilde belgelendirebilmek için uzantı yöntemi Produces çağrılır. Ancak, TypedResults yardımcı işlev uç noktanın meta verilerini otomatik olarak dahil ettiğinden, Results<T1, Tn> tür birleşimini aşağıdaki kodda gösterildiği gibi döndürebilirsiniz.

app.MapGet("/orders/{orderId}", Results<BadRequest, Ok<Order>> (int orderId)
    => orderId > 999 ? TypedResults.BadRequest() : TypedResults.Ok(new Order(orderId)));

Yerleşik sonuçlar

Results ve TypedResults statik sınıflarında ortak sonuç yardımcıları bulunmaktadır. TypedResults'i geri döndürmek, Results'i geri döndürmeye tercih edilir. Daha fazla bilgi için bkz . TypedResults vs Results.

Aşağıdaki bölümlerde ortak sonuç yardımcılarının kullanımı gösterilmektedir.

JSON veri formatı

app.MapGet("/hello", () => Results.Json(new { Message = "Hello World" }));

WriteAsJsonAsync JSON döndürmenin alternatif bir yoludur:

app.MapGet("/", (HttpContext context) => context.Response.WriteAsJsonAsync
    (new { Message = "Hello World" }));

Özel Durum Kodu

app.MapGet("/405", () => Results.StatusCode(405));

İç Sunucu Hatası

app.MapGet("/500", () => Results.InternalServerError("Something went wrong!"));

Yukarıdaki örnek 500 durum kodu döndürür.

Sorun ve Doğrulama Sorunu

app.MapGet("/problem", () =>
{
    var extensions = new List<KeyValuePair<string, object?>> { new("test", "value") };
    return TypedResults.Problem("This is an error with extensions", 
                                                extensions: extensions);
});

IProblemDetailsService kullanarak doğrulama hatası yanıtlarını özelleştirme

Bir uygulamayla IProblemDetailsService minimum API doğrulama mantığından gelen hata yanıtlarını özelleştirin. Daha tutarlı ve kullanıcıya özgü hata yanıtlarını etkinleştirmek için bu hizmeti uygulamanızın hizmet koleksiyonuna kaydedin. .NET 10'da ASP.NET Core'da en az API doğrulaması desteği sunulmuştur.

Özel doğrulama hatası yanıtları uygulamak için:

  • Varsayılan uygulamayı uygulama IProblemDetailsService veya kullanma
  • Hizmeti DI kapsayıcısında kaydetme
  • Doğrulama sistemi, doğrulama hatası yanıtlarını biçimlendirmek için kayıtlı hizmeti otomatik olarak kullanır

Aşağıdaki örnekte doğrulama hatası yanıtlarını özelleştirmek için öğesinin IProblemDetailsService nasıl kaydedilip yapılandırileceği gösterilmektedir:

using System.ComponentModel.DataAnnotations;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddProblemDetails(options =>
{
    options.CustomizeProblemDetails = context =>
    {
        if (context.ProblemDetails.Status == 400)
        {
            context.ProblemDetails.Title = "Validation error occurred";
            context.ProblemDetails.Extensions["support"] = "Contact support@example.com";
            context.ProblemDetails.Extensions["traceId"] = Guid.NewGuid().ToString();
        }
    };
});

Doğrulama hatası oluştuğunda, IProblemDetailsService geri çağırmaya eklenen özelleştirmeler de dahil olmak üzere hata yanıtını CustomizeProblemDetails oluşturmak için kullanılır.

Tam bir uygulama örneği için, ASP.NET Temel Minimum API'leri kullanarak doğrulama hatası yanıtlarını özelleştirmeyi gösteren En Düşük API örnek uygulamasınaIProblemDetailsService bakın.

Metin

app.MapGet("/text", () => Results.Text("This is some text"));

Yayın

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

var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () => 
{
    var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
    // Proxy the response as JSON
    return Results.Stream(stream, "application/json");
});

app.Run();

Results.Stream aşırı yüklemeler, temel HTTP yanıt akışına arabelleğe alma olmadan erişim sağlar. Aşağıdaki örnek, belirtilen görüntünün daha küçük bir boyutunu döndürmek için ImageSharp'ı kullanır:

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Processing;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapGet("/process-image/{strImage}", (string strImage, HttpContext http, CancellationToken token) =>
{
    http.Response.Headers.CacheControl = $"public,max-age={TimeSpan.FromHours(24).TotalSeconds}";
    return Results.Stream(stream => ResizeImageAsync(strImage, stream, token), "image/jpeg");
});

async Task ResizeImageAsync(string strImage, Stream stream, CancellationToken token)
{
    var strPath = $"wwwroot/img/{strImage}";
    using var image = await Image.LoadAsync(strPath, token);
    int width = image.Width / 2;
    int height = image.Height / 2;
    image.Mutate(x =>x.Resize(width, height));
    await image.SaveAsync(stream, JpegFormat.Instance, cancellationToken: token);
}

Aşağıdaki örnek, Azure Blob depolamadan bir görüntü akışı yapar:

app.MapGet("/stream-image/{containerName}/{blobName}", 
    async (string blobName, string containerName, CancellationToken token) =>
{
    var conStr = builder.Configuration["blogConStr"];
    BlobContainerClient blobContainerClient = new BlobContainerClient(conStr, containerName);
    BlobClient blobClient = blobContainerClient.GetBlobClient(blobName);
    return Results.Stream(await blobClient.OpenReadAsync(cancellationToken: token), "image/jpeg");
});

Aşağıdaki örnek, Azure Blob'dan video akışı yapar:

// GET /stream-video/videos/earth.mp4
app.MapGet("/stream-video/{containerName}/{blobName}",
     async (HttpContext http, CancellationToken token, string blobName, string containerName) =>
{
    var conStr = builder.Configuration["blogConStr"];
    BlobContainerClient blobContainerClient = new BlobContainerClient(conStr, containerName);
    BlobClient blobClient = blobContainerClient.GetBlobClient(blobName);
    
    var properties = await blobClient.GetPropertiesAsync(cancellationToken: token);
    
    DateTimeOffset lastModified = properties.Value.LastModified;
    long length = properties.Value.ContentLength;
    
    long etagHash = lastModified.ToFileTime() ^ length;
    var entityTag = new EntityTagHeaderValue('\"' + Convert.ToString(etagHash, 16) + '\"');
    
    http.Response.Headers.CacheControl = $"public,max-age={TimeSpan.FromHours(24).TotalSeconds}";

    return Results.Stream(await blobClient.OpenReadAsync(cancellationToken: token), 
        contentType: "video/mp4",
        lastModified: lastModified,
        entityTag: entityTag,
        enableRangeProcessing: true);
});

Server-Sent Olayları (SSE)

TypedResults.ServerSentEvents API'si Bir ServerSentEvents sonucunu döndürmeyi destekler.

Server-Sent Olaylar , sunucunun tek bir HTTP bağlantısı üzerinden istemciye olay iletileri akışı göndermesine olanak tanıyan bir sunucu gönderme teknolojisidir. .NET'te, olay iletileri bir olay türü, bir kimlik ve türünde SseItem<T>bir veri yükü içerebilen nesneler olarak T temsil edilir.

TypedResults sınıfı, bir sonuç döndürmek için kullanılabilecek ServerSentEvents adlı statik bir ServerSentEvents yönteme sahiptir. Bu yöntemin ilk parametresi, istemciye gönderilecek olay iletilerinin akışını temsil eden bir IAsyncEnumerable<SseItem<T>> parametredir.

Aşağıdaki örnekte, istemciye JSON nesneleri olarak kalp atış hızı olaylarının akışını döndürmek için API'nin nasıl kullanılacağı TypedResults.ServerSentEvents gösterilmektedir:

app.MapGet("sse-item", (CancellationToken cancellationToken) =>
{
    async IAsyncEnumerable<SseItem<int>> GetHeartRate(
        [EnumeratorCancellation] CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            var heartRate = Random.Shared.Next(60, 100);
            yield return new SseItem<int>(heartRate, eventType: "heartRate")
            {
                ReconnectionInterval = TimeSpan.FromMinutes(1)
            };
            await Task.Delay(2000, cancellationToken);
        }
    }

    return TypedResults.ServerSentEvents(GetHeartRate(cancellationToken));
});

Daha fazla bilgi için, API'yi kullanarak kalp atış hızı olaylarının bir akışını dize ve JSON nesneleri olarak istemciye döndüren TypedResults.ServerSentEvents bakın.

Yönlendir

app.MapGet("/old-path", () => Results.Redirect("/new-path"));

Dosya

app.MapGet("/download", () => Results.File("myfile.text"));

HttpResult arabirimleri

Aşağıdaki arabirimler, Microsoft.AspNetCore.Http ad alanındaki, filtre uygulamalarında yaygın bir desen olan çalışma zamanında IResult türünü algılamanın bir yolunu sağlar.

Aşağıda şu arabirimlerden birini kullanan bir filtre örneği verilmiştir:

app.MapGet("/weatherforecast", (int days) =>
{
    if (days <= 0)
    {
        return Results.BadRequest();
    }

    var forecast = Enumerable.Range(1, days).Select(index =>
       new WeatherForecast(DateTime.Now.AddDays(index), Random.Shared.Next(-20, 55), "Cool"))
        .ToArray();
    return Results.Ok(forecast);
}).
AddEndpointFilter(async (context, next) =>
{
    var result = await next(context);

    return result switch
    {
        IValueHttpResult<WeatherForecast[]> weatherForecastResult => new WeatherHttpResult(weatherForecastResult.Value),
        _ => result
    };
});

Daha fazla bilgi için bkz . Minimal API uygulamalarındaki filtreler ve IResult uygulama türleri.

Üst Bilgileri Değiştirme

Yanıt üst bilgilerini değiştirmek için HttpResponse nesnesini kullanın:

app.MapGet("/", (HttpContext context) => {
    // Set a custom header
    context.Response.Headers["X-Custom-Header"] = "CustomValue";

    // Set a known header
    context.Response.Headers.CacheControl = $"public,max-age=3600";

    return "Hello World";
});

Yanıtları özelleştirme

Uygulamalar özel IResult bir tür uygulayarak yanıtları denetleyebilir. Aşağıdaki kod, BIR HTML sonuç türü örneğidir:

using System.Net.Mime;
using System.Text;
static class ResultsExtensions
{
    public static IResult Html(this IResultExtensions resultExtensions, string html)
    {
        ArgumentNullException.ThrowIfNull(resultExtensions);

        return new HtmlResult(html);
    }
}

class HtmlResult : IResult
{
    private readonly string _html;

    public HtmlResult(string html)
    {
        _html = html;
    }

    public Task ExecuteAsync(HttpContext httpContext)
    {
        httpContext.Response.ContentType = MediaTypeNames.Text.Html;
        httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(_html);
        return httpContext.Response.WriteAsync(_html);
    }
}

Bu özel sonuçları daha bulunabilir hale getirmek için öğesine Microsoft.AspNetCore.Http.IResultExtensions bir uzantı yöntemi eklemenizi öneririz.

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

app.MapGet("/html", () => Results.Extensions.Html(@$"<!doctype html>
<html>
    <head><title>miniHTML</title></head>
    <body>
        <h1>Hello World</h1>
        <p>The time on the server is {DateTime.Now:O}</p>
    </body>
</html>"));

app.Run();

Ayrıca, özel bir IResult türü, IEndpointMetadataProvider arabirimini uygulayarak kendi ek açıklamasını sağlayabilir. Örneğin, aşağıdaki kod, uç nokta tarafından üretilen yanıtı açıklayan bir ek açıklamayı HtmlResult türüne ekler.

class HtmlResult : IResult, IEndpointMetadataProvider
{
    private readonly string _html;

    public HtmlResult(string html)
    {
        _html = html;
    }

    public Task ExecuteAsync(HttpContext httpContext)
    {
        httpContext.Response.ContentType = MediaTypeNames.Text.Html;
        httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(_html);
        return httpContext.Response.WriteAsync(_html);
    }

    public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
    {
        builder.Metadata.Add(new ProducesHtmlMetadata());
    }
}

ProducesHtmlMetadata, oluşturulan yanıt içerik türünü IProducesResponseTypeMetadata ve durum kodunu text/htmltanımlayan bir uygulamasıdır200 OK.

internal sealed class ProducesHtmlMetadata : IProducesResponseTypeMetadata
{
    public Type? Type => null;

    public int StatusCode => 200;

    public IEnumerable<string> ContentTypes { get; } = new[] { MediaTypeNames.Text.Html };
}

Alternatif bir yaklaşım, üretilen yanıtı açıklamada Microsoft.AspNetCore.Mvc.ProducesAttribute öğesini kullanmaktır. Aşağıdaki kod, PopulateMetadata yöntemini ProducesAttribute kullanacak şekilde değiştirir.

public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
    builder.Metadata.Add(new ProducesAttribute(MediaTypeNames.Text.Html));
}

JSON serileştirme seçeneklerini yapılandırma

Varsayılan olarak, minimum API uygulamaları JSON serileştirme ve seri durumdan çıkarma sırasında Web defaults seçeneklerini kullanır.

JSON serileştirme seçeneklerini genel olarak yapılandırma

Seçenekler, bir uygulama için küresel olarak ConfigureHttpJsonOptions çağrılarak yapılandırılabilir. Aşağıdaki örnek, ortak alanları ve JSON çıkışını biçimlendirmektedir.

var builder = WebApplication.CreateBuilder(args);

builder.Services.ConfigureHttpJsonOptions(options => {
    options.SerializerOptions.WriteIndented = true;
    options.SerializerOptions.IncludeFields = true;
});

var app = builder.Build();

app.MapPost("/", (Todo todo) => {
    if (todo is not null) {
        todo.Name = todo.NameField;
    }
    return todo;
});

app.Run();

class Todo {
    public string? Name { get; set; }
    public string? NameField;
    public bool IsComplete { get; set; }
}
// If the request body contains the following JSON:
//
// {"nameField":"Walk dog", "isComplete":false}
//
// The endpoint returns the following JSON:
//
// {
//    "name":"Walk dog",
//    "nameField":"Walk dog",
//    "isComplete":false
// }

Alanlar dahil olduğundan, yukarıdaki kod bunu okur NameField ve çıkış JSON'una ekler.

Uç nokta için JSON serileştirme seçeneklerini yapılandırma

Bir uç nokta için serileştirme seçeneklerini yapılandırmak amacıyla, Results.Json çağırın ve ona bir JsonSerializerOptions nesnesi gönderin, aşağıdaki örnekte gösterildiği gibi.

using System.Text.Json;

var app = WebApplication.Create();

var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
    { WriteIndented = true };

app.MapGet("/", () => 
    Results.Json(new Todo { Name = "Walk dog", IsComplete = false }, options));

app.Run();

class Todo
{
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}
// The endpoint returns the following JSON:
//
// {
//   "name":"Walk dog",
//   "isComplete":false
// }

Alternatif olarak, bir WriteAsJsonAsync nesnesini kabul eden bir JsonSerializerOptions aşırı yüklemesini kullanın. Aşağıdaki örnek, çıkış JSON'unu biçimlendirmek için bu aşırı yüklemeyi kullanır:

using System.Text.Json;

var app = WebApplication.Create();

var options = new JsonSerializerOptions(JsonSerializerDefaults.Web) {
    WriteIndented = true };

app.MapGet("/", (HttpContext context) =>
    context.Response.WriteAsJsonAsync<Todo>(
        new Todo { Name = "Walk dog", IsComplete = false }, options));

app.Run();

class Todo
{
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}
// The endpoint returns the following JSON:
//
// {
//   "name":"Walk dog",
//   "isComplete":false
// }

Ek Kaynaklar

En düşük uç noktalar aşağıdaki dönüş değeri türlerini destekler:

  1. string - Bu, Task<string> ve ValueTask<string>'yi içerir.
  2. T(Başka herhangi bir tür) - Bu, Task<T> ve ValueTask<T> içerir.
  3. IResult bazlı - Bu, Task<IResult> ve ValueTask<IResult> içerir.

string dönüş değerleri

Davranış İçerik Türü
Çerçeve, dizeyi doğrudan yanıta yazar. text/plain

Aşağıdaki, bir metin döndüren Hello world yol işleyicisini göz önünde bulundurun.

app.MapGet("/hello", () => "Hello World");

Durum 200 kodu, text/plain Content-Type üst bilgisi ve aşağıdaki içerikle döndürülür.

Hello World

T (Başka bir tür) değer döndürür

Davranış İçerik Türü
JSON çerçevesi yanıtı seri hale getirmektedir. application/json

Dize özelliği içeren Message anonim bir tür döndüren aşağıdaki yol işleyicisini göz önünde bulundurun.

app.MapGet("/hello", () => new { Message = "Hello World" });

Durum 200 kodu, application/json Content-Type üst bilgisi ve aşağıdaki içerikle döndürülür.

{"message":"Hello World"}

IResult dönüş değerleri

Davranış İçerik Türü
Çerçeve IResult.ExecuteAsync'i çağırır. Uygulama tarafından karar verilmiştir IResult .

Arabirimi, IResult HTTP uç noktasının sonucunu temsil eden bir sözleşme tanımlar. Statik Results sınıfı ve statik TypedResults , farklı yanıt türlerini temsil eden çeşitli IResult nesneler oluşturmak için kullanılır.

TypedResults ve Sonuçlar

Results ve TypedResults statik sınıfları benzer sonuç yardımcı kümeleri sağlar. TypedResults sınıfı, sınıfının Results eşdeğeridir. Ancak, Results yardımcılarının dönüş türü IResult iken, her TypedResults yardımcının dönüş türü, IResult uygulama türlerinden biridir. Fark, Results yardımcılar için somut tür gerektiğinde, örneğin birim testi için, bir dönüştürme yapılması gerektiği anlamına gelir. Uygulama türleri ad alanında Microsoft.AspNetCore.Http.HttpResults tanımlanır.

Döndürmek TypedResults yerine Results aşağıdaki avantajlara sahiptir:

  • TypedResults yardımcılar, katı bir şekilde türlendirilmiş nesneler döndürerek kod okunabilirliğini artırır, birim testlerini geliştirir ve çalışma zamanı hatalarının olasılığını azaltır.
  • Uygulama türü , uç noktayı açıklamak üzere OpenAPI için yanıt türü meta verilerini otomatik olarak sağlar.

Beklenen JSON yanıtına sahip bir 200 OK durum kodunun oluşturulduğu aşağıdaki uç noktayı göz önünde bulundurun.

app.MapGet("/hello", () => Results.Ok(new Message() { Text = "Hello World!" }))
    .Produces<Message>();

Bu uç noktayı doğru bir şekilde belgelendirebilmek için uzantılar yöntemi Produces çağrılır. Ancak, aşağıdaki kodda gösterildiği gibi Produces yerine TypedResults kullanılırsa, Results çağrısı yapılması gerekmez. TypedResults otomatik olarak uç nokta için meta verileri sağlar.

app.MapGet("/hello2", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));

Yanıt türünü açıklama hakkında daha fazla bilgi için bkz . Minimum API'lerde OpenAPI desteği.

Daha önce belirtildiği gibi, TypedResults kullanırken bir dönüştürme gerekli değildir. Aşağıdaki minimal API'yi, bir TypedResults sınıfı döndüren örnek olarak göz önünde bulundurun.

public static async Task<Ok<Todo[]>> GetAllTodos(TodoGroupDbContext database)
{
    var todos = await database.Todos.ToArrayAsync();
    return TypedResults.Ok(todos);
}

Aşağıdaki test tam beton türünü denetler.

[Fact]
public async Task GetAllReturnsTodosFromDatabase()
{
    // Arrange
    await using var context = new MockDb().CreateDbContext();

    context.Todos.Add(new Todo
    {
        Id = 1,
        Title = "Test title 1",
        Description = "Test description 1",
        IsDone = false
    });

    context.Todos.Add(new Todo
    {
        Id = 2,
        Title = "Test title 2",
        Description = "Test description 2",
        IsDone = true
    });

    await context.SaveChangesAsync();

    // Act
    var result = await TodoEndpointsV1.GetAllTodos(context);

    //Assert
    Assert.IsType<Ok<Todo[]>>(result);
    
    Assert.NotNull(result.Value);
    Assert.NotEmpty(result.Value);
    Assert.Collection(result.Value, todo1 =>
    {
        Assert.Equal(1, todo1.Id);
        Assert.Equal("Test title 1", todo1.Title);
        Assert.False(todo1.IsDone);
    }, todo2 =>
    {
        Assert.Equal(2, todo2.Id);
        Assert.Equal("Test title 2", todo2.Title);
        Assert.True(todo2.IsDone);
    });
}

Tüm Results üzerindeki yöntemler imzalarında IResult döndürdüğünden, derleyici, bir uç noktadan farklı sonuçlar dönerken bu durumu istek temsilcisi dönüş türü olarak otomatik bir şekilde çıkarsar. TypedResults, bu tür temsilcilerden Results<T1, TN> kullanımını gerektirir.

Aşağıdaki yöntem, dönülen nesnelerin gerçek somut türleri farklı olsa bile, hem Results.Ok hem de Results.NotFound döndüren IResult olarak bildirildiğinden derlenir.

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

Aşağıdaki yöntem derlenmez, çünkü TypedResults.Ok ve TypedResults.NotFound farklı türler döndüren olarak bildirilir ve derleyici en iyi eşleşen türü çıkarsamaya çalışmaz:

app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
     await db.Todos.FindAsync(id)
     is Todo todo
        ? TypedResults.Ok(todo)
        : TypedResults.NotFound());

TypedResults'yi kullanmak için dönüş türü tam olarak bildirilmelidir; bu durum asenkron olduğunda Task<> sarmalayıcı gerektirir. Kullanmak TypedResults daha ayrıntılıdır, ancak tür bilgilerinin statik olarak kullanılabilir olması ve bu nedenle OpenAPI'ye kendi kendini açıklayabilme özelliğine sahip olması için bu bir avantajdır:

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

Sonuçlar<TResult1, TResultN>

Aşağıdaki durumlarda Results<TResult1, TResultN> yerine IResult uç nokta işleyicisi dönüş türü olarak kullanın:

  • Uç nokta işleyicisinden birden çok IResult uygulama türü döndürülür.
  • Statik TypedResult sınıfı, IResult nesnelerini yaratmak için kullanılır.

Bu alternatif, genel birleşim türleri otomatik olarak uç nokta meta verilerini koruduğundan döndürmekten IResult daha iyidir. Results<TResult1, TResultN> Birleşim türleri örtük atama işleçleri uyguladığından, derleyici genel bağımsız değişkenlerde belirtilen türleri otomatik olarak birleşim türünün bir örneğine dönüştürebilir.

Bu, bir yol işleyicisinin aslında yalnızca bildirdiği sonuçları döndürdüğüne ilişkin derleme zamanı denetimi sağlamanın ek avantajına sahiptir. Genel bağımsız değişkenlerden biri olarak bildirilmemiş bir tür döndürmeye Results<> çalışmak derleme hatasıyla sonuçlanır.

Aşağıdaki uç noktayı, 400 BadRequest, orderId değerinden büyük olduğunda 999 durum kodunun döndürüldüğü göz önünde bulundurun. Aksi takdirde, beklenen içeriğe sahip bir 200 OK oluşturur.

app.MapGet("/orders/{orderId}", IResult (int orderId)
    => orderId > 999 ? TypedResults.BadRequest() : TypedResults.Ok(new Order(orderId)))
    .Produces(400)
    .Produces<Order>();

Bu uç noktayı doğru bir şekilde belgelendirebilmek için uzantı yöntemi Produces çağrılır. Ancak, TypedResults yardımcı işlev uç noktanın meta verilerini otomatik olarak dahil ettiğinden, Results<T1, Tn> tür birleşimini aşağıdaki kodda gösterildiği gibi döndürebilirsiniz.

app.MapGet("/orders/{orderId}", Results<BadRequest, Ok<Order>> (int orderId)
    => orderId > 999 ? TypedResults.BadRequest() : TypedResults.Ok(new Order(orderId)));

Yerleşik sonuçlar

Results ve TypedResults statik sınıflarında ortak sonuç yardımcıları bulunmaktadır. TypedResults'i geri döndürmek, Results'i geri döndürmeye tercih edilir. Daha fazla bilgi için bkz . TypedResults vs Results.

Aşağıdaki bölümlerde ortak sonuç yardımcılarının kullanımı gösterilmektedir.

JSON veri formatı

app.MapGet("/hello", () => Results.Json(new { Message = "Hello World" }));

WriteAsJsonAsync JSON döndürmenin alternatif bir yoludur:

app.MapGet("/", (HttpContext context) => context.Response.WriteAsJsonAsync
    (new { Message = "Hello World" }));

Özel Durum Kodu

app.MapGet("/405", () => Results.StatusCode(405));

İç Sunucu Hatası

app.MapGet("/500", () => Results.InternalServerError("Something went wrong!"));

Yukarıdaki örnek 500 durum kodu döndürür.

Sorun ve Doğrulama Sorunu

app.MapGet("/problem", () =>
{
    var extensions = new List<KeyValuePair<string, object?>> { new("test", "value") };
    return TypedResults.Problem("This is an error with extensions", 
                                                extensions: extensions);
});

Metin

app.MapGet("/text", () => Results.Text("This is some text"));

Yayın

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

var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () => 
{
    var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
    // Proxy the response as JSON
    return Results.Stream(stream, "application/json");
});

app.Run();

Results.Stream aşırı yüklemeler, temel HTTP yanıt akışına arabelleğe alma olmadan erişim sağlar. Aşağıdaki örnek, belirtilen görüntünün daha küçük bir boyutunu döndürmek için ImageSharp'ı kullanır:

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Processing;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapGet("/process-image/{strImage}", (string strImage, HttpContext http, CancellationToken token) =>
{
    http.Response.Headers.CacheControl = $"public,max-age={TimeSpan.FromHours(24).TotalSeconds}";
    return Results.Stream(stream => ResizeImageAsync(strImage, stream, token), "image/jpeg");
});

async Task ResizeImageAsync(string strImage, Stream stream, CancellationToken token)
{
    var strPath = $"wwwroot/img/{strImage}";
    using var image = await Image.LoadAsync(strPath, token);
    int width = image.Width / 2;
    int height = image.Height / 2;
    image.Mutate(x =>x.Resize(width, height));
    await image.SaveAsync(stream, JpegFormat.Instance, cancellationToken: token);
}

Aşağıdaki örnek, Azure Blob depolamadan bir görüntü akışı yapar:

app.MapGet("/stream-image/{containerName}/{blobName}", 
    async (string blobName, string containerName, CancellationToken token) =>
{
    var conStr = builder.Configuration["blogConStr"];
    BlobContainerClient blobContainerClient = new BlobContainerClient(conStr, containerName);
    BlobClient blobClient = blobContainerClient.GetBlobClient(blobName);
    return Results.Stream(await blobClient.OpenReadAsync(cancellationToken: token), "image/jpeg");
});

Aşağıdaki örnek, Azure Blob'dan video akışı yapar:

// GET /stream-video/videos/earth.mp4
app.MapGet("/stream-video/{containerName}/{blobName}",
     async (HttpContext http, CancellationToken token, string blobName, string containerName) =>
{
    var conStr = builder.Configuration["blogConStr"];
    BlobContainerClient blobContainerClient = new BlobContainerClient(conStr, containerName);
    BlobClient blobClient = blobContainerClient.GetBlobClient(blobName);
    
    var properties = await blobClient.GetPropertiesAsync(cancellationToken: token);
    
    DateTimeOffset lastModified = properties.Value.LastModified;
    long length = properties.Value.ContentLength;
    
    long etagHash = lastModified.ToFileTime() ^ length;
    var entityTag = new EntityTagHeaderValue('\"' + Convert.ToString(etagHash, 16) + '\"');
    
    http.Response.Headers.CacheControl = $"public,max-age={TimeSpan.FromHours(24).TotalSeconds}";

    return Results.Stream(await blobClient.OpenReadAsync(cancellationToken: token), 
        contentType: "video/mp4",
        lastModified: lastModified,
        entityTag: entityTag,
        enableRangeProcessing: true);
});

Yönlendir

app.MapGet("/old-path", () => Results.Redirect("/new-path"));

Dosya

app.MapGet("/download", () => Results.File("myfile.text"));

HttpResult arabirimleri

Aşağıdaki arabirimler, Microsoft.AspNetCore.Http ad alanındaki, filtre uygulamalarında yaygın bir desen olan çalışma zamanında IResult türünü algılamanın bir yolunu sağlar.

Aşağıda şu arabirimlerden birini kullanan bir filtre örneği verilmiştir:

app.MapGet("/weatherforecast", (int days) =>
{
    if (days <= 0)
    {
        return Results.BadRequest();
    }

    var forecast = Enumerable.Range(1, days).Select(index =>
       new WeatherForecast(DateTime.Now.AddDays(index), Random.Shared.Next(-20, 55), "Cool"))
        .ToArray();
    return Results.Ok(forecast);
}).
AddEndpointFilter(async (context, next) =>
{
    var result = await next(context);

    return result switch
    {
        IValueHttpResult<WeatherForecast[]> weatherForecastResult => new WeatherHttpResult(weatherForecastResult.Value),
        _ => result
    };
});

Daha fazla bilgi için bkz . Minimal API uygulamalarındaki filtreler ve IResult uygulama türleri.

Üst Bilgileri Değiştirme

Yanıt üst bilgilerini değiştirmek için HttpResponse nesnesini kullanın:

app.MapGet("/", (HttpContext context) => {
    // Set a custom header
    context.Response.Headers["X-Custom-Header"] = "CustomValue";

    // Set a known header
    context.Response.Headers.CacheControl = $"public,max-age=3600";

    return "Hello World";
});

Yanıtları özelleştirme

Uygulamalar özel IResult bir tür uygulayarak yanıtları denetleyebilir. Aşağıdaki kod, BIR HTML sonuç türü örneğidir:

using System.Net.Mime;
using System.Text;
static class ResultsExtensions
{
    public static IResult Html(this IResultExtensions resultExtensions, string html)
    {
        ArgumentNullException.ThrowIfNull(resultExtensions);

        return new HtmlResult(html);
    }
}

class HtmlResult : IResult
{
    private readonly string _html;

    public HtmlResult(string html)
    {
        _html = html;
    }

    public Task ExecuteAsync(HttpContext httpContext)
    {
        httpContext.Response.ContentType = MediaTypeNames.Text.Html;
        httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(_html);
        return httpContext.Response.WriteAsync(_html);
    }
}

Bu özel sonuçları daha bulunabilir hale getirmek için öğesine Microsoft.AspNetCore.Http.IResultExtensions bir uzantı yöntemi eklemenizi öneririz.

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

app.MapGet("/html", () => Results.Extensions.Html(@$"<!doctype html>
<html>
    <head><title>miniHTML</title></head>
    <body>
        <h1>Hello World</h1>
        <p>The time on the server is {DateTime.Now:O}</p>
    </body>
</html>"));

app.Run();

Ayrıca, özel bir IResult türü, IEndpointMetadataProvider arabirimini uygulayarak kendi ek açıklamasını sağlayabilir. Örneğin, aşağıdaki kod, uç nokta tarafından üretilen yanıtı açıklayan bir ek açıklamayı HtmlResult türüne ekler.

class HtmlResult : IResult, IEndpointMetadataProvider
{
    private readonly string _html;

    public HtmlResult(string html)
    {
        _html = html;
    }

    public Task ExecuteAsync(HttpContext httpContext)
    {
        httpContext.Response.ContentType = MediaTypeNames.Text.Html;
        httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(_html);
        return httpContext.Response.WriteAsync(_html);
    }

    public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
    {
        builder.Metadata.Add(new ProducesHtmlMetadata());
    }
}

ProducesHtmlMetadata, oluşturulan yanıt içerik türünü IProducesResponseTypeMetadata ve durum kodunu text/htmltanımlayan bir uygulamasıdır200 OK.

internal sealed class ProducesHtmlMetadata : IProducesResponseTypeMetadata
{
    public Type? Type => null;

    public int StatusCode => 200;

    public IEnumerable<string> ContentTypes { get; } = new[] { MediaTypeNames.Text.Html };
}

Alternatif bir yaklaşım, üretilen yanıtı açıklamada Microsoft.AspNetCore.Mvc.ProducesAttribute öğesini kullanmaktır. Aşağıdaki kod, PopulateMetadata yöntemini ProducesAttribute kullanacak şekilde değiştirir.

public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
    builder.Metadata.Add(new ProducesAttribute(MediaTypeNames.Text.Html));
}

JSON serileştirme seçeneklerini yapılandırma

Varsayılan olarak, minimum API uygulamaları JSON serileştirme ve seri durumdan çıkarma sırasında Web defaults seçeneklerini kullanır.

JSON serileştirme seçeneklerini genel olarak yapılandırma

Seçenekler, bir uygulama için küresel olarak ConfigureHttpJsonOptions çağrılarak yapılandırılabilir. Aşağıdaki örnek, ortak alanları ve JSON çıkışını biçimlendirmektedir.

var builder = WebApplication.CreateBuilder(args);

builder.Services.ConfigureHttpJsonOptions(options => {
    options.SerializerOptions.WriteIndented = true;
    options.SerializerOptions.IncludeFields = true;
});

var app = builder.Build();

app.MapPost("/", (Todo todo) => {
    if (todo is not null) {
        todo.Name = todo.NameField;
    }
    return todo;
});

app.Run();

class Todo {
    public string? Name { get; set; }
    public string? NameField;
    public bool IsComplete { get; set; }
}
// If the request body contains the following JSON:
//
// {"nameField":"Walk dog", "isComplete":false}
//
// The endpoint returns the following JSON:
//
// {
//    "name":"Walk dog",
//    "nameField":"Walk dog",
//    "isComplete":false
// }

Alanlar dahil olduğundan, yukarıdaki kod bunu okur NameField ve çıkış JSON'una ekler.

Uç nokta için JSON serileştirme seçeneklerini yapılandırma

Bir uç nokta için serileştirme seçeneklerini yapılandırmak amacıyla, Results.Json çağırın ve ona bir JsonSerializerOptions nesnesi gönderin, aşağıdaki örnekte gösterildiği gibi.

using System.Text.Json;

var app = WebApplication.Create();

var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
    { WriteIndented = true };

app.MapGet("/", () => 
    Results.Json(new Todo { Name = "Walk dog", IsComplete = false }, options));

app.Run();

class Todo
{
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}
// The endpoint returns the following JSON:
//
// {
//   "name":"Walk dog",
//   "isComplete":false
// }

Alternatif olarak, bir WriteAsJsonAsync nesnesini kabul eden bir JsonSerializerOptions aşırı yüklemesini kullanın. Aşağıdaki örnek, çıkış JSON'unu biçimlendirmek için bu aşırı yüklemeyi kullanır:

using System.Text.Json;

var app = WebApplication.Create();

var options = new JsonSerializerOptions(JsonSerializerDefaults.Web) {
    WriteIndented = true };

app.MapGet("/", (HttpContext context) =>
    context.Response.WriteAsJsonAsync<Todo>(
        new Todo { Name = "Walk dog", IsComplete = false }, options));

app.Run();

class Todo
{
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}
// The endpoint returns the following JSON:
//
// {
//   "name":"Walk dog",
//   "isComplete":false
// }

Ek Kaynaklar

En düşük uç noktalar aşağıdaki dönüş değeri türlerini destekler:

  1. string - Bu, Task<string> ve ValueTask<string>'yi içerir.
  2. T(Başka herhangi bir tür) - Bu, Task<T> ve ValueTask<T> içerir.
  3. IResult bazlı - Bu, Task<IResult> ve ValueTask<IResult> içerir.

string dönüş değerleri

Davranış İçerik Türü
Çerçeve, dizeyi doğrudan yanıta yazar. text/plain

Aşağıdaki, bir metin döndüren Hello world yol işleyicisini göz önünde bulundurun.

app.MapGet("/hello", () => "Hello World");

Durum 200 kodu, text/plain Content-Type üst bilgisi ve aşağıdaki içerikle döndürülür.

Hello World

T (Başka bir tür) değer döndürür

Davranış İçerik Türü
JSON çerçevesi yanıtı seri hale getirmektedir. application/json

Dize özelliği içeren Message anonim bir tür döndüren aşağıdaki yol işleyicisini göz önünde bulundurun.

app.MapGet("/hello", () => new { Message = "Hello World" });

Durum 200 kodu, application/json Content-Type üst bilgisi ve aşağıdaki içerikle döndürülür.

{"message":"Hello World"}

IResult dönüş değerleri

Davranış İçerik Türü
Çerçeve IResult.ExecuteAsync'i çağırır. Uygulama tarafından karar verilmiştir IResult .

Arabirimi, IResult HTTP uç noktasının sonucunu temsil eden bir sözleşme tanımlar. Statik Results sınıfı ve statik TypedResults , farklı yanıt türlerini temsil eden çeşitli IResult nesneler oluşturmak için kullanılır.

TypedResults ve Sonuçlar

Results ve TypedResults statik sınıfları benzer sonuç yardımcı kümeleri sağlar. TypedResults sınıfı, sınıfının Results eşdeğeridir. Ancak, Results yardımcılarının dönüş türü IResult iken, her TypedResults yardımcının dönüş türü, IResult uygulama türlerinden biridir. Fark, Results yardımcılar için somut tür gerektiğinde, örneğin birim testi için, bir dönüştürme yapılması gerektiği anlamına gelir. Uygulama türleri ad alanında Microsoft.AspNetCore.Http.HttpResults tanımlanır.

Döndürmek TypedResults yerine Results aşağıdaki avantajlara sahiptir:

  • TypedResults yardımcılar, katı bir şekilde türlendirilmiş nesneler döndürerek kod okunabilirliğini artırır, birim testlerini geliştirir ve çalışma zamanı hatalarının olasılığını azaltır.
  • Uygulama türü , uç noktayı açıklamak üzere OpenAPI için yanıt türü meta verilerini otomatik olarak sağlar.

Beklenen JSON yanıtına sahip bir 200 OK durum kodunun oluşturulduğu aşağıdaki uç noktayı göz önünde bulundurun.

app.MapGet("/hello", () => Results.Ok(new Message() { Text = "Hello World!" }))
    .Produces<Message>();

Bu uç noktayı doğru bir şekilde belgelendirebilmek için uzantılar yöntemi Produces çağrılır. Ancak, aşağıdaki kodda gösterildiği gibi Produces yerine TypedResults kullanılırsa, Results çağrısı yapılması gerekmez. TypedResults otomatik olarak uç nokta için meta verileri sağlar.

app.MapGet("/hello2", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));

Yanıt türünü açıklama hakkında daha fazla bilgi için bkz . Minimum API'lerde OpenAPI desteği.

Daha önce belirtildiği gibi, TypedResults kullanırken bir dönüştürme gerekli değildir. Aşağıdaki minimal API'yi, bir TypedResults sınıfı döndüren örnek olarak göz önünde bulundurun.

public static async Task<Ok<Todo[]>> GetAllTodos(TodoGroupDbContext database)
{
    var todos = await database.Todos.ToArrayAsync();
    return TypedResults.Ok(todos);
}

Aşağıdaki test tam beton türünü denetler.

[Fact]
public async Task GetAllReturnsTodosFromDatabase()
{
    // Arrange
    await using var context = new MockDb().CreateDbContext();

    context.Todos.Add(new Todo
    {
        Id = 1,
        Title = "Test title 1",
        Description = "Test description 1",
        IsDone = false
    });

    context.Todos.Add(new Todo
    {
        Id = 2,
        Title = "Test title 2",
        Description = "Test description 2",
        IsDone = true
    });

    await context.SaveChangesAsync();

    // Act
    var result = await TodoEndpointsV1.GetAllTodos(context);

    //Assert
    Assert.IsType<Ok<Todo[]>>(result);
    
    Assert.NotNull(result.Value);
    Assert.NotEmpty(result.Value);
    Assert.Collection(result.Value, todo1 =>
    {
        Assert.Equal(1, todo1.Id);
        Assert.Equal("Test title 1", todo1.Title);
        Assert.False(todo1.IsDone);
    }, todo2 =>
    {
        Assert.Equal(2, todo2.Id);
        Assert.Equal("Test title 2", todo2.Title);
        Assert.True(todo2.IsDone);
    });
}

Tüm Results üzerindeki yöntemler imzalarında IResult döndürdüğünden, derleyici, bir uç noktadan farklı sonuçlar dönerken bu durumu istek temsilcisi dönüş türü olarak otomatik bir şekilde çıkarsar. TypedResults, bu tür temsilcilerden Results<T1, TN> kullanımını gerektirir.

Aşağıdaki yöntem, dönülen nesnelerin gerçek somut türleri farklı olsa bile, hem Results.Ok hem de Results.NotFound döndüren IResult olarak bildirildiğinden derlenir.

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

Aşağıdaki yöntem derlenmez, çünkü TypedResults.Ok ve TypedResults.NotFound farklı türler döndüren olarak bildirilir ve derleyici en iyi eşleşen türü çıkarsamaya çalışmaz:

app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
     await db.Todos.FindAsync(id)
     is Todo todo
        ? TypedResults.Ok(todo)
        : TypedResults.NotFound());

TypedResults'yi kullanmak için dönüş türü tam olarak bildirilmelidir; bu durum asenkron olduğunda Task<> sarmalayıcı gerektirir. Kullanmak TypedResults daha ayrıntılıdır, ancak tür bilgilerinin statik olarak kullanılabilir olması ve bu nedenle OpenAPI'ye kendi kendini açıklayabilme özelliğine sahip olması için bu bir avantajdır:

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

Sonuçlar<TResult1, TResultN>

Aşağıdaki durumlarda Results<TResult1, TResultN> yerine IResult uç nokta işleyicisi dönüş türü olarak kullanın:

  • Uç nokta işleyicisinden birden çok IResult uygulama türü döndürülür.
  • Statik TypedResult sınıfı, IResult nesnelerini yaratmak için kullanılır.

Bu alternatif, genel birleşim türleri otomatik olarak uç nokta meta verilerini koruduğundan döndürmekten IResult daha iyidir. Results<TResult1, TResultN> Birleşim türleri örtük atama işleçleri uyguladığından, derleyici genel bağımsız değişkenlerde belirtilen türleri otomatik olarak birleşim türünün bir örneğine dönüştürebilir.

Bu, bir yol işleyicisinin aslında yalnızca bildirdiği sonuçları döndürdüğüne ilişkin derleme zamanı denetimi sağlamanın ek avantajına sahiptir. Genel bağımsız değişkenlerden biri olarak bildirilmemiş bir tür döndürmeye Results<> çalışmak derleme hatasıyla sonuçlanır.

Aşağıdaki uç noktayı, 400 BadRequest, orderId değerinden büyük olduğunda 999 durum kodunun döndürüldüğü göz önünde bulundurun. Aksi takdirde, beklenen içeriğe sahip bir 200 OK oluşturur.

app.MapGet("/orders/{orderId}", IResult (int orderId)
    => orderId > 999 ? TypedResults.BadRequest() : TypedResults.Ok(new Order(orderId)))
    .Produces(400)
    .Produces<Order>();

Bu uç noktayı doğru bir şekilde belgelendirebilmek için uzantı yöntemi Produces çağrılır. Ancak, TypedResults yardımcı işlev uç noktanın meta verilerini otomatik olarak dahil ettiğinden, Results<T1, Tn> tür birleşimini aşağıdaki kodda gösterildiği gibi döndürebilirsiniz.

app.MapGet("/orders/{orderId}", Results<BadRequest, Ok<Order>> (int orderId) 
    => orderId > 999 ? TypedResults.BadRequest() : TypedResults.Ok(new Order(orderId)));

Yerleşik sonuçlar

Results ve TypedResults statik sınıflarında ortak sonuç yardımcıları bulunmaktadır. TypedResults'i geri döndürmek, Results'i geri döndürmeye tercih edilir. Daha fazla bilgi için bkz . TypedResults vs Results.

Aşağıdaki bölümlerde ortak sonuç yardımcılarının kullanımı gösterilmektedir.

JSON veri formatı

app.MapGet("/hello", () => Results.Json(new { Message = "Hello World" }));

WriteAsJsonAsync JSON döndürmenin alternatif bir yoludur:

app.MapGet("/", (HttpContext context) => context.Response.WriteAsJsonAsync
    (new { Message = "Hello World" }));

Özel Durum Kodu

app.MapGet("/405", () => Results.StatusCode(405));

Metin

app.MapGet("/text", () => Results.Text("This is some text"));

Yayın

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

var proxyClient = new HttpClient();
app.MapGet("/pokemon", async () => 
{
    var stream = await proxyClient.GetStreamAsync("http://contoso/pokedex.json");
    // Proxy the response as JSON
    return Results.Stream(stream, "application/json");
});

app.Run();

Results.Stream aşırı yüklemeler, temel HTTP yanıt akışına arabelleğe alma olmadan erişim sağlar. Aşağıdaki örnek, belirtilen görüntünün daha küçük bir boyutunu döndürmek için ImageSharp'ı kullanır:

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Processing;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapGet("/process-image/{strImage}", (string strImage, HttpContext http, CancellationToken token) =>
{
    http.Response.Headers.CacheControl = $"public,max-age={TimeSpan.FromHours(24).TotalSeconds}";
    return Results.Stream(stream => ResizeImageAsync(strImage, stream, token), "image/jpeg");
});

async Task ResizeImageAsync(string strImage, Stream stream, CancellationToken token)
{
    var strPath = $"wwwroot/img/{strImage}";
    using var image = await Image.LoadAsync(strPath, token);
    int width = image.Width / 2;
    int height = image.Height / 2;
    image.Mutate(x =>x.Resize(width, height));
    await image.SaveAsync(stream, JpegFormat.Instance, cancellationToken: token);
}

Aşağıdaki örnek, Azure Blob depolamadan bir görüntü akışı yapar:

app.MapGet("/stream-image/{containerName}/{blobName}", 
    async (string blobName, string containerName, CancellationToken token) =>
{
    var conStr = builder.Configuration["blogConStr"];
    BlobContainerClient blobContainerClient = new BlobContainerClient(conStr, containerName);
    BlobClient blobClient = blobContainerClient.GetBlobClient(blobName);
    return Results.Stream(await blobClient.OpenReadAsync(cancellationToken: token), "image/jpeg");
});

Aşağıdaki örnek, Azure Blob'dan video akışı yapar:

// GET /stream-video/videos/earth.mp4
app.MapGet("/stream-video/{containerName}/{blobName}",
     async (HttpContext http, CancellationToken token, string blobName, string containerName) =>
{
    var conStr = builder.Configuration["blogConStr"];
    BlobContainerClient blobContainerClient = new BlobContainerClient(conStr, containerName);
    BlobClient blobClient = blobContainerClient.GetBlobClient(blobName);
    
    var properties = await blobClient.GetPropertiesAsync(cancellationToken: token);
    
    DateTimeOffset lastModified = properties.Value.LastModified;
    long length = properties.Value.ContentLength;
    
    long etagHash = lastModified.ToFileTime() ^ length;
    var entityTag = new EntityTagHeaderValue('\"' + Convert.ToString(etagHash, 16) + '\"');
    
    http.Response.Headers.CacheControl = $"public,max-age={TimeSpan.FromHours(24).TotalSeconds}";

    return Results.Stream(await blobClient.OpenReadAsync(cancellationToken: token), 
        contentType: "video/mp4",
        lastModified: lastModified,
        entityTag: entityTag,
        enableRangeProcessing: true);
});

Yönlendir

app.MapGet("/old-path", () => Results.Redirect("/new-path"));

Dosya

app.MapGet("/download", () => Results.File("myfile.text"));

HttpResult arabirimleri

Aşağıdaki arabirimler, Microsoft.AspNetCore.Http ad alanındaki, filtre uygulamalarında yaygın bir desen olan çalışma zamanında IResult türünü algılamanın bir yolunu sağlar.

Aşağıda şu arabirimlerden birini kullanan bir filtre örneği verilmiştir:

app.MapGet("/weatherforecast", (int days) =>
{
    if (days <= 0)
    {
        return Results.BadRequest();
    }

    var forecast = Enumerable.Range(1, days).Select(index =>
       new WeatherForecast(DateTime.Now.AddDays(index), Random.Shared.Next(-20, 55), "Cool"))
        .ToArray();
    return Results.Ok(forecast);
}).
AddEndpointFilter(async (context, next) =>
{
    var result = await next(context);

    return result switch
    {
        IValueHttpResult<WeatherForecast[]> weatherForecastResult => new WeatherHttpResult(weatherForecastResult.Value),
        _ => result
    };
});

Daha fazla bilgi için bkz . Minimal API uygulamalarındaki filtreler ve IResult uygulama türleri.

Yanıtları özelleştirme

Uygulamalar özel IResult bir tür uygulayarak yanıtları denetleyebilir. Aşağıdaki kod, BIR HTML sonuç türü örneğidir:

using System.Net.Mime;
using System.Text;
static class ResultsExtensions
{
    public static IResult Html(this IResultExtensions resultExtensions, string html)
    {
        ArgumentNullException.ThrowIfNull(resultExtensions);

        return new HtmlResult(html);
    }
}

class HtmlResult : IResult
{
    private readonly string _html;

    public HtmlResult(string html)
    {
        _html = html;
    }

    public Task ExecuteAsync(HttpContext httpContext)
    {
        httpContext.Response.ContentType = MediaTypeNames.Text.Html;
        httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(_html);
        return httpContext.Response.WriteAsync(_html);
    }
}

Bu özel sonuçları daha bulunabilir hale getirmek için öğesine Microsoft.AspNetCore.Http.IResultExtensions bir uzantı yöntemi eklemenizi öneririz.

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

app.MapGet("/html", () => Results.Extensions.Html(@$"<!doctype html>
<html>
    <head><title>miniHTML</title></head>
    <body>
        <h1>Hello World</h1>
        <p>The time on the server is {DateTime.Now:O}</p>
    </body>
</html>"));

app.Run();

Ayrıca, özel bir IResult türü, IEndpointMetadataProvider arabirimini uygulayarak kendi ek açıklamasını sağlayabilir. Örneğin, aşağıdaki kod, uç nokta tarafından üretilen yanıtı açıklayan bir ek açıklamayı HtmlResult türüne ekler.

class HtmlResult : IResult, IEndpointMetadataProvider
{
    private readonly string _html;

    public HtmlResult(string html)
    {
        _html = html;
    }

    public Task ExecuteAsync(HttpContext httpContext)
    {
        httpContext.Response.ContentType = MediaTypeNames.Text.Html;
        httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(_html);
        return httpContext.Response.WriteAsync(_html);
    }

    public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
    {
        builder.Metadata.Add(new ProducesHtmlMetadata());
    }
}

ProducesHtmlMetadata, oluşturulan yanıt içerik türünü IProducesResponseTypeMetadata ve durum kodunu text/htmltanımlayan bir uygulamasıdır200 OK.

internal sealed class ProducesHtmlMetadata : IProducesResponseTypeMetadata
{
    public Type? Type => null;

    public int StatusCode => 200;

    public IEnumerable<string> ContentTypes { get; } = new[] { MediaTypeNames.Text.Html };
}

Alternatif bir yaklaşım, üretilen yanıtı açıklamada Microsoft.AspNetCore.Mvc.ProducesAttribute öğesini kullanmaktır. Aşağıdaki kod, PopulateMetadata yöntemini ProducesAttribute kullanacak şekilde değiştirir.

public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
    builder.Metadata.Add(new ProducesAttribute(MediaTypeNames.Text.Html));
}

JSON serileştirme seçeneklerini yapılandırma

Varsayılan olarak, minimum API uygulamaları JSON serileştirme ve seri durumdan çıkarma sırasında Web defaults seçeneklerini kullanır.

JSON serileştirme seçeneklerini genel olarak yapılandırma

Seçenekler, bir uygulama için küresel olarak ConfigureHttpJsonOptions çağrılarak yapılandırılabilir. Aşağıdaki örnek, ortak alanları ve JSON çıkışını biçimlendirmektedir.

var builder = WebApplication.CreateBuilder(args);

builder.Services.ConfigureHttpJsonOptions(options => {
    options.SerializerOptions.WriteIndented = true;
    options.SerializerOptions.IncludeFields = true;
});

var app = builder.Build();

app.MapPost("/", (Todo todo) => {
    if (todo is not null) {
        todo.Name = todo.NameField;
    }
    return todo;
});

app.Run();

class Todo {
    public string? Name { get; set; }
    public string? NameField;
    public bool IsComplete { get; set; }
}
// If the request body contains the following JSON:
//
// {"nameField":"Walk dog", "isComplete":false}
//
// The endpoint returns the following JSON:
//
// {
//    "name":"Walk dog",
//    "nameField":"Walk dog",
//    "isComplete":false
// }

Alanlar dahil olduğundan, yukarıdaki kod bunu okur NameField ve çıkış JSON'una ekler.

Uç nokta için JSON serileştirme seçeneklerini yapılandırma

Bir uç nokta için serileştirme seçeneklerini yapılandırmak amacıyla, Results.Json çağırın ve ona bir JsonSerializerOptions nesnesi gönderin, aşağıdaki örnekte gösterildiği gibi.

using System.Text.Json;

var app = WebApplication.Create();

var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
    { WriteIndented = true };

app.MapGet("/", () => 
    Results.Json(new Todo { Name = "Walk dog", IsComplete = false }, options));

app.Run();

class Todo
{
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}
// The endpoint returns the following JSON:
//
// {
//   "name":"Walk dog",
//   "isComplete":false
// }

Alternatif olarak, bir WriteAsJsonAsync nesnesini kabul eden bir JsonSerializerOptions aşırı yüklemesini kullanın. Aşağıdaki örnek, çıkış JSON'unu biçimlendirmek için bu aşırı yüklemeyi kullanır:

using System.Text.Json;

var app = WebApplication.Create();

var options = new JsonSerializerOptions(JsonSerializerDefaults.Web) {
    WriteIndented = true };

app.MapGet("/", (HttpContext context) =>
    context.Response.WriteAsJsonAsync<Todo>(
        new Todo { Name = "Walk dog", IsComplete = false }, options));

app.Run();

class Todo
{
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}
// The endpoint returns the following JSON:
//
// {
//   "name":"Walk dog",
//   "isComplete":false
// }

Ek Kaynaklar