Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
Det här är inte den senaste versionen av den här artikeln. För den aktuella utgåvan, se version .NET 9 av den här artikeln.
Varning
Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i .NET och .NET Core Support Policy. För den aktuella utgåvan, se version .NET 9 av den här artikeln.
Viktig
Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.
För den aktuella utgåvan, se version .NET 9 av den här artikeln.
Den här artikeln beskriver hur du skapar svar för minimala API-slutpunkter i ASP.NET Core. Minimala API:er ger flera sätt att returnera data och HTTP-statuskoder.
Minimala slutpunkter stöder följande typer av returvärden:
- 
              string– Detta inkluderarTask<string>ochValueTask<string>.
- 
              T(alla andra typer) – Detta inkluderarTask<T>ochValueTask<T>.
- 
              IResultbaserad – Detta inkluderarTask<IResult>ochValueTask<IResult>.
Viktig
Från och med ASP.NET Core 10 omdirigeras inte längre kända API-slutpunkter till inloggningssidor när autentisering används cookie . I stället returnerar de statuskoder för 401/403. Mer information finns i BETEENDE för API-slutpunktsautentisering i ASP.NET Core.
              string Returnera värden
| Uppförande | Innehållstyp | 
|---|---|
| Ramverket skriver strängen direkt till svaret. | text/plain | 
Överväg följande routningshanterare, som returnerar en Hello world text.
app.MapGet("/hello", () => "Hello World");
Statuskoden 200 returneras med text/plain innehållstypsrubrik och följande innehåll.
Hello World
              T (alla andra typer) returnerar värden
| Uppförande | Innehållstyp | 
|---|---|
| Ramverket JSON-serialiserar svaret. | application/json | 
Överväg följande routningshanterare, som returnerar en anonym typ som innehåller en Message strängegenskap.
app.MapGet("/hello", () => new { Message = "Hello World" });
Statuskoden 200 returneras med application/json innehållstypsrubrik och följande innehåll.
{"message":"Hello World"}
              IResult Returnera värden
| Uppförande | Innehållstyp | 
|---|---|
| Ramverket anropar IResult.ExecuteAsync. | Bestäms av IResultimplementeringen. | 
Gränssnittet IResult definierar ett kontrakt som representerar resultatet av en HTTP-slutpunkt. Den statiska Results-klassen och den statiska TypedResults- används för att skapa olika IResult objekt som representerar olika typer av svar.
TypedResults vs Resultat
De Results- och TypedResults statiska klasserna ger liknande uppsättningar resultathjälpare. Klassen TypedResults är den av typen motsvarigheten till klassen Results. 
              Results-hjälpens returtyp är dock IResult, medan varje TypedResults-hjälpens returtyp är en av de IResult implementeringstyperna. Skillnaden innebär att en konvertering krävs för Results hjälpverktyg när den konkreta typen behövs, till exempel för enhetstestning. Implementeringstyperna definieras i namnområdet Microsoft.AspNetCore.Http.HttpResults.
Att returnera TypedResults i stället för Results har följande fördelar:
- 
              TypedResultshjälpare returnerar starkt skrivna objekt, vilket kan förbättra kodens läsbarhet, enhetstestning och minska risken för körningsfel.
- Implementeringstypen tillhandahåller automatiskt svarstypmetadata för OpenAPI- för att beskriva slutpunkten.
Överväg följande slutpunkt, för vilken en 200 OK statuskod med det förväntade JSON-svaret skapas.
app.MapGet("/hello", () => Results.Ok(new Message() { Text = "Hello World!" }))
    .Produces<Message>();
För att kunna dokumentera den här slutpunkten korrekt anropas tilläggsmetoden Produces. Det är dock inte nödvändigt att anropa Produces om TypedResults används i stället för Results, enligt följande kod. 
              TypedResults tillhandahåller automatiskt metadata för slutpunkten.
app.MapGet("/hello2", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Mer information om hur du beskriver en svarstyp finns i OpenAPI-stöd i minimala API:er.
Exempel på testresultattyper finns i testdokumentationen.
Eftersom alla metoder på Results returnerar IResult i sina signaturer, drar kompilatorn automatiskt slutsatsen att detta ska vara returneringstypen för begärandedelegaten när olika resultat returneras från en enda slutpunkt. 
              TypedResults kräver att Results<T1, TN> används av sådana ombud.
Följande metod kompileras eftersom både Results.Ok och Results.NotFound deklareras som returvärden av IResult, även om de konkreta typerna för de returnerade objekten är olika:
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
    await db.Todos.FindAsync(id)
        is Todo todo
            ? Results.Ok(todo)
            : Results.NotFound());
Följande metod kompileras inte eftersom TypedResults.Ok och TypedResults.NotFound deklareras som returnerade olika typer och kompilatorn inte försöker härleda den bästa matchningstypen:
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
     await db.Todos.FindAsync(id)
     is Todo todo
        ? TypedResults.Ok(todo)
        : TypedResults.NotFound());
Om du vill använda TypedResultsmåste returtypen vara helt deklarerad. När metoden är asynkron kräver deklarationen att returtypen omsluts i en Task<>. Att använda TypedResults är mer utförligt, men det är kompromissen för att typinformationen ska vara statiskt tillgänglig och därmed kunna självbeskriva till OpenAPI:
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());
Resultat<TResult1, TResultN>
Använd Results<TResult1, TResultN> som slutpunktshanterarens returtyp i stället för IResult när:
- Flera IResultimplementeringstyper returneras från slutpunktshanteraren.
- Den statiska TypedResult-klassen används för att skapaIResultobjekt.
Det här alternativet är bättre än att returnera IResult eftersom de allmänna unionstyperna automatiskt behåller slutpunktsmetadata. Och eftersom Results<TResult1, TResultN>-unionstyperna implementerar implicita överföringsoperatorer kan kompilatorn automatiskt konvertera de typer som anges i de generiska argumenten till en instans av uniontypen.
Detta har den extra fördelen med att tillhandahålla kompileringstidskontroll att en routningshanterare faktiskt bara returnerar de resultat som den deklarerar att den gör. Försök att returnera en typ som inte deklareras som ett av de allmänna argumenten för att Results<> resulterar i ett kompileringsfel.
Tänk på följande slutpunkt, för vilken en 400 BadRequest statuskod returneras när orderId är större än 999. Annars producerar den en 200 OK med det förväntade innehållet.
app.MapGet("/orders/{orderId}", IResult (int orderId)
    => orderId > 999 ? TypedResults.BadRequest() : TypedResults.Ok(new Order(orderId)))
    .Produces(400)
    .Produces<Order>();
För att kunna dokumentera den här slutpunkten korrekt anropas tilläggsmetoden Produces. Men, eftersom TypedResults-hjälparskriptet automatiskt innehåller metadata för slutpunkten, kan du i stället returnera Results<T1, Tn>-unionstypen, som visas i följande kod.
app.MapGet("/orders/{orderId}", Results<BadRequest, Ok<Order>> (int orderId)
    => orderId > 999 ? TypedResults.BadRequest() : TypedResults.Ok(new Order(orderId)));
Inbyggda resultat
Vanliga resultathjälpare finns i Results- och TypedResults statiska klasser. Att returnera TypedResults föredras framför att returnera Results. Mer information finns i TypedResults vs Results.
Följande avsnitt visar användningen av vanliga resultathjälpare.
JSON
app.MapGet("/hello", () => Results.Json(new { Message = "Hello World" }));
WriteAsJsonAsync är ett alternativt sätt att returnera JSON:
app.MapGet("/", (HttpContext context) => context.Response.WriteAsJsonAsync
    (new { Message = "Hello World" }));
Anpassad statuskod
app.MapGet("/405", () => Results.StatusCode(405));
Internt serverfel
app.MapGet("/500", () => Results.InternalServerError("Something went wrong!"));
Föregående exempel returnerar en statuskod på 500.
Problem och valideringsproblem
app.MapGet("/problem", () =>
{
    var extensions = new List<KeyValuePair<string, object?>> { new("test", "value") };
    return TypedResults.Problem("This is an error with extensions", 
                                                extensions: extensions);
});
Anpassa svar på verifieringsfel med IProblemDetailsService
Anpassa felsvar från minimal API-valideringslogik med en IProblemDetailsService implementering. Registrera den här tjänsten i programmets tjänstsamling för att aktivera mer konsekventa och användarspecifika felsvar. Stöd för minimal API-validering introducerades i ASP.NET Core i .NET 10.
Så här implementerar du anpassade svar på valideringsfel:
- Implementera IProblemDetailsService eller använda standardimplementeringen
- Registrera tjänsten i DI-containern
- Valideringssystemet använder automatiskt den registrerade tjänsten för att formatera valideringsfelsvar
I följande exempel visas hur du registrerar och konfigurerar för att anpassa svar på IProblemDetailsService valideringsfel:
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();
        }
    };
});
När ett valideringsfel inträffar IProblemDetailsService används det för att generera felsvaret, inklusive eventuella anpassningar som läggs till i återanropet CustomizeProblemDetails .
Ett komplett appexempel finns i exempelappen Minimal API som visar hur du anpassar svar på valideringsfel med hjälp av IProblemDetailsService i ASP.NET Core Minimal-API:er.
SMS
app.MapGet("/text", () => Results.Text("This is some text"));
Strömning
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 överlagringar ger åtkomst till den underliggande HTTP-svarsströmmen utan buffring. I följande exempel används ImageSharp- för att returnera en reducerad storlek på den angivna bilden:
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);
}
I följande exempel strömmas en avbildning från Azure Blob Storage-:
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");
});
I följande exempel strömmas en video från en Azure Blob:
// 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-sända händelser (SSE)
API:et TypedResults.ServerSentEvents stöder returnering av ett ServerSentEvents-resultat.
              Server-Sent Events är en server push-teknik som gör att en server kan skicka en ström av händelsemeddelanden till en klient via en enda HTTP-anslutning. I .NET representeras händelsemeddelandena som SseItem<T> objekt, som kan innehålla en händelsetyp, ett ID och en datanyttolast av typen T.
Klassen TypedResults har en statisk metod med namnet ServerSentEvents som kan användas för att returnera ett ServerSentEvents resultat. Den första parametern för den här metoden är en IAsyncEnumerable<SseItem<T>> som representerar strömmen av händelsemeddelanden som ska skickas till klienten.
I följande exempel visas hur du använder API:et TypedResults.ServerSentEvents för att returnera en ström av pulshändelser som JSON-objekt till klienten:
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));
});
Mer information finns i exempelappen Minimal API som använder API:et TypedResults.ServerSentEvents för att returnera en ström av pulshändelser som sträng- ServerSentEventsoch JSON-objekt till klienten.
Omdirigera
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
Fil
app.MapGet("/download", () => Results.File("myfile.text"));
HttpResult-gränssnitt
Följande gränssnitt i namnområdet Microsoft.AspNetCore.Http ger ett sätt att identifiera IResult typen under körning, vilket är ett vanligt mönster i filterimplementeringar.
- IContentTypeHttpResult
- IFileHttpResult
- INestedHttpResult
- IStatusCodeHttpResult
- IValueHttpResult
- IValueHttpResult<TValue>
Här är ett exempel på ett filter som använder något av följande gränssnitt:
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
    };
});
Mer information finns i filter i minimala API-appar och IResult-implementeringstyper.
Ändra rubriker
Använd HttpResponse-objektet för att ändra svarshuvuden:
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";
});
Anpassa svar
Program kan styra svar genom att implementera en anpassad IResult typ. Följande kod är ett exempel på en HTML-resultattyp:
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);
    }
}
Vi rekommenderar att du lägger till en tilläggsmetod till Microsoft.AspNetCore.Http.IResultExtensions för att göra dessa anpassade resultat mer upptäckbara.
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();
Dessutom kan en anpassad IResult typ ge en egen anteckning genom att implementera IEndpointMetadataProvider-gränssnittet. Följande kod lägger till en annotation till den föregående HtmlResult-typen som beskriver svaret som genereras av slutpunkten.
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 är en implementering av IProducesResponseTypeMetadata som definierar den producerade svarsinnehållstypen text/html och statuskoden 200 OK.
internal sealed class ProducesHtmlMetadata : IProducesResponseTypeMetadata
{
    public Type? Type => null;
    public int StatusCode => 200;
    public IEnumerable<string> ContentTypes { get; } = new[] { MediaTypeNames.Text.Html };
}
En alternativ metod är att använda Microsoft.AspNetCore.Mvc.ProducesAttribute för att beskriva det producerade svaret. Följande kod ändrar metoden PopulateMetadata för att använda ProducesAttribute.
public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
    builder.Metadata.Add(new ProducesAttribute(MediaTypeNames.Text.Html));
}
Konfigurera alternativ för JSON-serialisering
Som standard använder minimala API-appar Web defaults alternativ under JSON-serialisering och deserialisering.
Konfigurera JSON-serialiseringsalternativ globalt
Alternativ kan konfigureras globalt för en app genom att anropa ConfigureHttpJsonOptions. Följande exempel innehåller offentliga fält och formaterar JSON-utdata.
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
// }
Eftersom fält ingår läser koden ovan NameField och innehåller den i JSON-utdata.
Konfigurera JSON-serialiseringsalternativ för en slutpunkt
Om du vill konfigurera serialiseringsalternativ för en slutpunkt anropar du Results.Json och skickar ett JsonSerializerOptions objekt till den, som du ser i följande exempel:
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
// }
Du kan istället använda en överbelastning av WriteAsJsonAsync som tar emot en JsonSerializerOptions-objekt. I följande exempel används den här överlagringen för att formatera utdata-JSON:
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
// }
Ytterligare resurser
Minimala slutpunkter stöder följande typer av returvärden:
- 
              string– Detta inkluderarTask<string>ochValueTask<string>.
- 
              T(alla andra typer) – Detta inkluderarTask<T>ochValueTask<T>.
- 
              IResultbaserad – Detta inkluderarTask<IResult>ochValueTask<IResult>.
              string Returnera värden
| Uppförande | Innehållstyp | 
|---|---|
| Ramverket skriver strängen direkt till svaret. | text/plain | 
Överväg följande routningshanterare, som returnerar en Hello world text.
app.MapGet("/hello", () => "Hello World");
Statuskoden 200 returneras med text/plain innehållstypsrubrik och följande innehåll.
Hello World
              T (alla andra typer) returnerar värden
| Uppförande | Innehållstyp | 
|---|---|
| Ramverket JSON-serialiserar svaret. | application/json | 
Överväg följande routningshanterare, som returnerar en anonym typ som innehåller en Message strängegenskap.
app.MapGet("/hello", () => new { Message = "Hello World" });
Statuskoden 200 returneras med application/json innehållstypsrubrik och följande innehåll.
{"message":"Hello World"}
              IResult Returnera värden
| Uppförande | Innehållstyp | 
|---|---|
| Ramverket anropar IResult.ExecuteAsync. | Bestäms av IResultimplementeringen. | 
Gränssnittet IResult definierar ett kontrakt som representerar resultatet av en HTTP-slutpunkt. Den statiska Results-klassen och den statiska TypedResults- används för att skapa olika IResult objekt som representerar olika typer av svar.
TypedResults vs Resultat
De Results- och TypedResults statiska klasserna ger liknande uppsättningar resultathjälpare. Klassen TypedResults är den av typen motsvarigheten till klassen Results. 
              Results-hjälpens returtyp är dock IResult, medan varje TypedResults-hjälpens returtyp är en av de IResult implementeringstyperna. Skillnaden innebär att en konvertering krävs för Results hjälpverktyg när den konkreta typen behövs, till exempel för enhetstestning. Implementeringstyperna definieras i namnområdet Microsoft.AspNetCore.Http.HttpResults.
Att returnera TypedResults i stället för Results har följande fördelar:
- 
              TypedResultshjälpare returnerar starkt skrivna objekt, vilket kan förbättra kodens läsbarhet, enhetstestning och minska risken för körningsfel.
- Implementeringstypen tillhandahåller automatiskt svarstypmetadata för OpenAPI- för att beskriva slutpunkten.
Överväg följande slutpunkt, för vilken en 200 OK statuskod med det förväntade JSON-svaret skapas.
app.MapGet("/hello", () => Results.Ok(new Message() { Text = "Hello World!" }))
    .Produces<Message>();
För att kunna dokumentera den här slutpunkten korrekt anropas tilläggsmetoden Produces. Det är dock inte nödvändigt att anropa Produces om TypedResults används i stället för Results, enligt följande kod. 
              TypedResults tillhandahåller automatiskt metadata för slutpunkten.
app.MapGet("/hello2", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Mer information om hur du beskriver en svarstyp finns i OpenAPI-stöd i minimala API:er.
Som tidigare nämnts behövs ingen konvertering när du använder TypedResults. Överväg följande minimala API som returnerar en TypedResults-klass
public static async Task<Ok<Todo[]>> GetAllTodos(TodoGroupDbContext database)
{
    var todos = await database.Todos.ToArrayAsync();
    return TypedResults.Ok(todos);
}
Följande test kontrollerar den fullständiga betongtypen:
[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);
    });
}
Eftersom alla metoder på Results returnerar IResult i sina signaturer, drar kompilatorn automatiskt slutsatsen att detta ska vara returneringstypen för begärandedelegaten när olika resultat returneras från en enda slutpunkt. 
              TypedResults kräver att Results<T1, TN> används av sådana ombud.
Följande metod kompileras eftersom både Results.Ok och Results.NotFound deklareras som returvärden av IResult, även om de konkreta typerna för de returnerade objekten är olika:
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
    await db.Todos.FindAsync(id)
        is Todo todo
            ? Results.Ok(todo)
            : Results.NotFound());
Följande metod kompileras inte eftersom TypedResults.Ok och TypedResults.NotFound deklareras som returnerade olika typer och kompilatorn inte försöker härleda den bästa matchningstypen:
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
     await db.Todos.FindAsync(id)
     is Todo todo
        ? TypedResults.Ok(todo)
        : TypedResults.NotFound());
För att använda TypedResultsmåste returtypen vara helt deklarerad, vilket när den används asynkront kräver wrappern Task<>. Att använda TypedResults är mer utförligt, men det är kompromissen för att typinformationen ska vara statiskt tillgänglig och därmed kunna självbeskriva till OpenAPI:
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());
Resultat<TResult1, TResultN>
Använd Results<TResult1, TResultN> som slutpunktshanterarens returtyp i stället för IResult när:
- Flera IResultimplementeringstyper returneras från slutpunktshanteraren.
- Den statiska TypedResult-klassen används för att skapaIResultobjekt.
Det här alternativet är bättre än att returnera IResult eftersom de allmänna unionstyperna automatiskt behåller slutpunktsmetadata. Och eftersom Results<TResult1, TResultN>-unionstyperna implementerar implicita överföringsoperatorer kan kompilatorn automatiskt konvertera de typer som anges i de generiska argumenten till en instans av uniontypen.
Detta har den extra fördelen med att tillhandahålla kompileringstidskontroll att en routningshanterare faktiskt bara returnerar de resultat som den deklarerar att den gör. Försök att returnera en typ som inte deklareras som ett av de allmänna argumenten för att Results<> resulterar i ett kompileringsfel.
Tänk på följande slutpunkt, för vilken en 400 BadRequest statuskod returneras när orderId är större än 999. Annars producerar den en 200 OK med det förväntade innehållet.
app.MapGet("/orders/{orderId}", IResult (int orderId)
    => orderId > 999 ? TypedResults.BadRequest() : TypedResults.Ok(new Order(orderId)))
    .Produces(400)
    .Produces<Order>();
För att kunna dokumentera den här slutpunkten korrekt anropas tilläggsmetoden Produces. Men, eftersom TypedResults-hjälparskriptet automatiskt innehåller metadata för slutpunkten, kan du i stället returnera Results<T1, Tn>-unionstypen, som visas i följande kod.
app.MapGet("/orders/{orderId}", Results<BadRequest, Ok<Order>> (int orderId)
    => orderId > 999 ? TypedResults.BadRequest() : TypedResults.Ok(new Order(orderId)));
Inbyggda resultat
Vanliga resultathjälpare finns i Results- och TypedResults statiska klasser. Att returnera TypedResults föredras framför att returnera Results. Mer information finns i TypedResults vs Results.
Följande avsnitt visar användningen av vanliga resultathjälpare.
JSON
app.MapGet("/hello", () => Results.Json(new { Message = "Hello World" }));
WriteAsJsonAsync är ett alternativt sätt att returnera JSON:
app.MapGet("/", (HttpContext context) => context.Response.WriteAsJsonAsync
    (new { Message = "Hello World" }));
Anpassad statuskod
app.MapGet("/405", () => Results.StatusCode(405));
Internt serverfel
app.MapGet("/500", () => Results.InternalServerError("Something went wrong!"));
Föregående exempel returnerar en statuskod på 500.
Problem och valideringsproblem
app.MapGet("/problem", () =>
{
    var extensions = new List<KeyValuePair<string, object?>> { new("test", "value") };
    return TypedResults.Problem("This is an error with extensions", 
                                                extensions: extensions);
});
SMS
app.MapGet("/text", () => Results.Text("This is some text"));
Strömning
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 överlagringar ger åtkomst till den underliggande HTTP-svarsströmmen utan buffring. I följande exempel används ImageSharp- för att returnera en reducerad storlek på den angivna bilden:
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);
}
I följande exempel strömmas en avbildning från Azure Blob Storage-:
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");
});
I följande exempel strömmas en video från en Azure Blob:
// 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);
});
Omdirigera
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
Fil
app.MapGet("/download", () => Results.File("myfile.text"));
HttpResult-gränssnitt
Följande gränssnitt i namnområdet Microsoft.AspNetCore.Http ger ett sätt att identifiera IResult typen under körning, vilket är ett vanligt mönster i filterimplementeringar.
- IContentTypeHttpResult
- IFileHttpResult
- INestedHttpResult
- IStatusCodeHttpResult
- IValueHttpResult
- IValueHttpResult<TValue>
Här är ett exempel på ett filter som använder något av följande gränssnitt:
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
    };
});
Mer information finns i filter i minimala API-appar och IResult-implementeringstyper.
Ändra rubriker
Använd HttpResponse-objektet för att ändra svarshuvuden:
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";
});
Anpassa svar
Program kan styra svar genom att implementera en anpassad IResult typ. Följande kod är ett exempel på en HTML-resultattyp:
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);
    }
}
Vi rekommenderar att du lägger till en tilläggsmetod till Microsoft.AspNetCore.Http.IResultExtensions för att göra dessa anpassade resultat mer upptäckbara.
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();
Dessutom kan en anpassad IResult typ ge en egen anteckning genom att implementera IEndpointMetadataProvider-gränssnittet. Följande kod lägger till en annotation till den föregående HtmlResult-typen som beskriver svaret som genereras av slutpunkten.
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 är en implementering av IProducesResponseTypeMetadata som definierar den producerade svarsinnehållstypen text/html och statuskoden 200 OK.
internal sealed class ProducesHtmlMetadata : IProducesResponseTypeMetadata
{
    public Type? Type => null;
    public int StatusCode => 200;
    public IEnumerable<string> ContentTypes { get; } = new[] { MediaTypeNames.Text.Html };
}
En alternativ metod är att använda Microsoft.AspNetCore.Mvc.ProducesAttribute för att beskriva det producerade svaret. Följande kod ändrar metoden PopulateMetadata för att använda ProducesAttribute.
public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
    builder.Metadata.Add(new ProducesAttribute(MediaTypeNames.Text.Html));
}
Konfigurera alternativ för JSON-serialisering
Som standard använder minimala API-appar Web defaults alternativ under JSON-serialisering och deserialisering.
Konfigurera JSON-serialiseringsalternativ globalt
Alternativ kan konfigureras globalt för en app genom att anropa ConfigureHttpJsonOptions. Följande exempel innehåller offentliga fält och formaterar JSON-utdata.
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
// }
Eftersom fält ingår läser koden ovan NameField och innehåller den i JSON-utdata.
Konfigurera JSON-serialiseringsalternativ för en slutpunkt
Om du vill konfigurera serialiseringsalternativ för en slutpunkt anropar du Results.Json och skickar ett JsonSerializerOptions objekt till den, som du ser i följande exempel:
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
// }
Du kan istället använda en överbelastning av WriteAsJsonAsync som tar emot en JsonSerializerOptions-objekt. I följande exempel används den här överlagringen för att formatera utdata-JSON:
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
// }
Ytterligare resurser
Minimala slutpunkter stöder följande typer av returvärden:
- 
              string– Detta inkluderarTask<string>ochValueTask<string>.
- 
              T(alla andra typer) – Detta inkluderarTask<T>ochValueTask<T>.
- 
              IResultbaserad – Detta inkluderarTask<IResult>ochValueTask<IResult>.
              string Returnera värden
| Uppförande | Innehållstyp | 
|---|---|
| Ramverket skriver strängen direkt till svaret. | text/plain | 
Överväg följande routningshanterare, som returnerar en Hello world text.
app.MapGet("/hello", () => "Hello World");
Statuskoden 200 returneras med text/plain innehållstypsrubrik och följande innehåll.
Hello World
              T (alla andra typer) returnerar värden
| Uppförande | Innehållstyp | 
|---|---|
| Ramverket JSON-serialiserar svaret. | application/json | 
Överväg följande routningshanterare, som returnerar en anonym typ som innehåller en Message strängegenskap.
app.MapGet("/hello", () => new { Message = "Hello World" });
Statuskoden 200 returneras med application/json innehållstypsrubrik och följande innehåll.
{"message":"Hello World"}
              IResult Returnera värden
| Uppförande | Innehållstyp | 
|---|---|
| Ramverket anropar IResult.ExecuteAsync. | Bestäms av IResultimplementeringen. | 
Gränssnittet IResult definierar ett kontrakt som representerar resultatet av en HTTP-slutpunkt. Den statiska Results-klassen och den statiska TypedResults- används för att skapa olika IResult objekt som representerar olika typer av svar.
TypedResults vs Resultat
De Results- och TypedResults statiska klasserna ger liknande uppsättningar resultathjälpare. Klassen TypedResults är den av typen motsvarigheten till klassen Results. 
              Results-hjälpens returtyp är dock IResult, medan varje TypedResults-hjälpens returtyp är en av de IResult implementeringstyperna. Skillnaden innebär att en konvertering krävs för Results hjälpverktyg när den konkreta typen behövs, till exempel för enhetstestning. Implementeringstyperna definieras i namnområdet Microsoft.AspNetCore.Http.HttpResults.
Att returnera TypedResults i stället för Results har följande fördelar:
- 
              TypedResultshjälpare returnerar starkt skrivna objekt, vilket kan förbättra kodens läsbarhet, enhetstestning och minska risken för körningsfel.
- Implementeringstypen tillhandahåller automatiskt svarstypmetadata för OpenAPI- för att beskriva slutpunkten.
Överväg följande slutpunkt, för vilken en 200 OK statuskod med det förväntade JSON-svaret skapas.
app.MapGet("/hello", () => Results.Ok(new Message() { Text = "Hello World!" }))
    .Produces<Message>();
För att kunna dokumentera den här slutpunkten korrekt anropas tilläggsmetoden Produces. Det är dock inte nödvändigt att anropa Produces om TypedResults används i stället för Results, enligt följande kod. 
              TypedResults tillhandahåller automatiskt metadata för slutpunkten.
app.MapGet("/hello2", () => TypedResults.Ok(new Message() { Text = "Hello World!" }));
Mer information om hur du beskriver en svarstyp finns i OpenAPI-stöd i minimala API:er.
Som tidigare nämnts behövs ingen konvertering när du använder TypedResults. Överväg följande minimala API som returnerar en TypedResults-klass
public static async Task<Ok<Todo[]>> GetAllTodos(TodoGroupDbContext database)
{
    var todos = await database.Todos.ToArrayAsync();
    return TypedResults.Ok(todos);
}
Följande test kontrollerar den fullständiga betongtypen:
[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);
    });
}
Eftersom alla metoder på Results returnerar IResult i sina signaturer, drar kompilatorn automatiskt slutsatsen att detta ska vara returneringstypen för begärandedelegaten när olika resultat returneras från en enda slutpunkt. 
              TypedResults kräver att Results<T1, TN> används av sådana ombud.
Följande metod kompileras eftersom både Results.Ok och Results.NotFound deklareras som returvärden av IResult, även om de konkreta typerna för de returnerade objekten är olika:
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
    await db.Todos.FindAsync(id)
        is Todo todo
            ? Results.Ok(todo)
            : Results.NotFound());
Följande metod kompileras inte eftersom TypedResults.Ok och TypedResults.NotFound deklareras som returnerade olika typer och kompilatorn inte försöker härleda den bästa matchningstypen:
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
     await db.Todos.FindAsync(id)
     is Todo todo
        ? TypedResults.Ok(todo)
        : TypedResults.NotFound());
För att använda TypedResultsmåste returtypen vara helt deklarerad, vilket när den används asynkront kräver wrappern Task<>. Att använda TypedResults är mer utförligt, men det är kompromissen för att typinformationen ska vara statiskt tillgänglig och därmed kunna självbeskriva till OpenAPI:
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());
Resultat<TResult1, TResultN>
Använd Results<TResult1, TResultN> som slutpunktshanterarens returtyp i stället för IResult när:
- Flera IResultimplementeringstyper returneras från slutpunktshanteraren.
- Den statiska TypedResult-klassen används för att skapaIResultobjekt.
Det här alternativet är bättre än att returnera IResult eftersom de allmänna unionstyperna automatiskt behåller slutpunktsmetadata. Och eftersom Results<TResult1, TResultN>-unionstyperna implementerar implicita överföringsoperatorer kan kompilatorn automatiskt konvertera de typer som anges i de generiska argumenten till en instans av uniontypen.
Detta har den extra fördelen med att tillhandahålla kompileringstidskontroll att en routningshanterare faktiskt bara returnerar de resultat som den deklarerar att den gör. Försök att returnera en typ som inte deklareras som ett av de allmänna argumenten för att Results<> resulterar i ett kompileringsfel.
Tänk på följande slutpunkt, för vilken en 400 BadRequest statuskod returneras när orderId är större än 999. Annars producerar den en 200 OK med det förväntade innehållet.
app.MapGet("/orders/{orderId}", IResult (int orderId)
    => orderId > 999 ? TypedResults.BadRequest() : TypedResults.Ok(new Order(orderId)))
    .Produces(400)
    .Produces<Order>();
För att kunna dokumentera den här slutpunkten korrekt anropas tilläggsmetoden Produces. Men, eftersom TypedResults-hjälparskriptet automatiskt innehåller metadata för slutpunkten, kan du i stället returnera Results<T1, Tn>-unionstypen, som visas i följande kod.
app.MapGet("/orders/{orderId}", Results<BadRequest, Ok<Order>> (int orderId) 
    => orderId > 999 ? TypedResults.BadRequest() : TypedResults.Ok(new Order(orderId)));
Inbyggda resultat
Vanliga resultathjälpare finns i Results- och TypedResults statiska klasser. Att returnera TypedResults föredras framför att returnera Results. Mer information finns i TypedResults vs Results.
Följande avsnitt visar användningen av vanliga resultathjälpare.
JSON
app.MapGet("/hello", () => Results.Json(new { Message = "Hello World" }));
WriteAsJsonAsync är ett alternativt sätt att returnera JSON:
app.MapGet("/", (HttpContext context) => context.Response.WriteAsJsonAsync
    (new { Message = "Hello World" }));
Anpassad statuskod
app.MapGet("/405", () => Results.StatusCode(405));
SMS
app.MapGet("/text", () => Results.Text("This is some text"));
Strömning
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 överlagringar ger åtkomst till den underliggande HTTP-svarsströmmen utan buffring. I följande exempel används ImageSharp- för att returnera en reducerad storlek på den angivna bilden:
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);
}
I följande exempel strömmas en avbildning från Azure Blob Storage-:
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");
});
I följande exempel strömmas en video från en Azure Blob:
// 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);
});
Omdirigera
app.MapGet("/old-path", () => Results.Redirect("/new-path"));
Fil
app.MapGet("/download", () => Results.File("myfile.text"));
HttpResult-gränssnitt
Följande gränssnitt i namnområdet Microsoft.AspNetCore.Http ger ett sätt att identifiera IResult typen under körning, vilket är ett vanligt mönster i filterimplementeringar.
- IContentTypeHttpResult
- IFileHttpResult
- INestedHttpResult
- IStatusCodeHttpResult
- IValueHttpResult
- IValueHttpResult<TValue>
Här är ett exempel på ett filter som använder något av följande gränssnitt:
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
    };
});
Mer information finns i filter i minimala API-appar och IResult-implementeringstyper.
Anpassa svar
Program kan styra svar genom att implementera en anpassad IResult typ. Följande kod är ett exempel på en HTML-resultattyp:
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);
    }
}
Vi rekommenderar att du lägger till en tilläggsmetod till Microsoft.AspNetCore.Http.IResultExtensions för att göra dessa anpassade resultat mer upptäckbara.
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();
Dessutom kan en anpassad IResult typ ge en egen anteckning genom att implementera IEndpointMetadataProvider-gränssnittet. Följande kod lägger till en annotation till den föregående HtmlResult-typen som beskriver svaret som genereras av slutpunkten.
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 är en implementering av IProducesResponseTypeMetadata som definierar den producerade svarsinnehållstypen text/html och statuskoden 200 OK.
internal sealed class ProducesHtmlMetadata : IProducesResponseTypeMetadata
{
    public Type? Type => null;
    public int StatusCode => 200;
    public IEnumerable<string> ContentTypes { get; } = new[] { MediaTypeNames.Text.Html };
}
En alternativ metod är att använda Microsoft.AspNetCore.Mvc.ProducesAttribute för att beskriva det producerade svaret. Följande kod ändrar metoden PopulateMetadata för att använda ProducesAttribute.
public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
    builder.Metadata.Add(new ProducesAttribute(MediaTypeNames.Text.Html));
}
Konfigurera alternativ för JSON-serialisering
Som standard använder minimala API-appar Web defaults alternativ under JSON-serialisering och deserialisering.
Konfigurera JSON-serialiseringsalternativ globalt
Alternativ kan konfigureras globalt för en app genom att anropa ConfigureHttpJsonOptions. Följande exempel innehåller offentliga fält och formaterar JSON-utdata.
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
// }
Eftersom fält ingår läser koden ovan NameField och innehåller den i JSON-utdata.
Konfigurera JSON-serialiseringsalternativ för en slutpunkt
Om du vill konfigurera serialiseringsalternativ för en slutpunkt anropar du Results.Json och skickar ett JsonSerializerOptions objekt till den, som du ser i följande exempel:
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
// }
Du kan istället använda en överbelastning av WriteAsJsonAsync som tar emot en JsonSerializerOptions-objekt. I följande exempel används den här överlagringen för att formatera utdata-JSON:
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
// }
Ytterligare resurser
ASP.NET Core