Rückgabetypen für Controlleraktionen in der ASP.NET Core-Web-API

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)

ASP.NET Core bietet die folgenden Optionen für Rückgabetypen für Web-API-Controlleraktionen:

In diesem Artikel wird erläutert, wann die einzelnen Rückgabetypen am sinnvollsten zu verwenden sind.

Spezifischer Typ

Die einfachste Aktion gibt einen primitiven oder komplexen Datentyp zurück (z. B. string oder einen benutzerdefinierten Objekttyp). Die folgende Aktion gibt eine Auflistung benutzerdefinierter Product-Objekte zurück:

[HttpGet]
public Task<List<Product>> Get() =>
    _productContext.Products.OrderBy(p => p.Name).ToListAsync();

Ohne bekannte Bedingungen, gegen die während Schutzmaßnahmen ergriffen werden müssen, reicht möglicherweise die Rückgabe eines bestimmten Typs aus. Die vorherige Aktion akzeptiert keine Parameter, weshalb eine Validierung von Parametereinschränkungen nicht erforderlich ist.

Wenn mehrere Rückgabetypen möglich sind, ist es üblich, einen ActionResult-Rückgabetyp mit dem primitiven oder komplexen Rückgabetyp zu mischen. Für diese Art von Aktion sind entweder IActionResult oder ActionResult<T> erforderlich. Dieser Artikel enthält einige Beispiele für mehrere Rückgabetypen.

Rückgabe von IEnumerable<T> oder IAsyncEnumerable<T>

Überlegungen zur Leistung finden Sie unter Rückgabe von IEnumerable<T> oder IAsyncEnumerable<T>.

ASP.NET Core puffert das Ergebnis von Aktionen, die IEnumerable<T> zurückgeben, bevor sie diese in die Antwort schreiben. Sie sollten den Rückgabetyp der Signatur der Aktion als IAsyncEnumerable<T> deklarieren, um eine asynchrone Iteration sicherzustellen. Letztendlich basiert der Iterationsmodus auf dem zugrunde liegenden konkreten Typ, der zurückgegeben wird, und der ausgewählte Formatierer beeinflusst die Verarbeitung des Ergebnisses:

  • Bei Verwendung des System.Text.Json-Formatierers verlässt sich MVC auf die Unterstützung, die System.Text.Json zum Streamen des Ergebnisses hinzugefügt hat.
  • Bei Verwendung von Newtonsoft.Json oder mit XML-based-Formatierern wird das Ergebnis gepuffert.

Sehen Sie sich die folgende Aktion an, die den Verkaufspreis enthaltende Produktdatensätze als IEnumerable<Product> zurückgibt:

[HttpGet("syncsale")]
public IEnumerable<Product> GetOnSaleProducts()
{
    var products = _productContext.Products.OrderBy(p => p.Name).ToList();

    foreach (var product in products)
    {
        if (product.IsOnSale)
        {
            yield return product;
        }
    }
}

Die IAsyncEnumerable<Product>-Entsprechung der vorherigen Aktion lautet wie folgt:

[HttpGet("asyncsale")]
public async IAsyncEnumerable<Product> GetOnSaleProductsAsync()
{
    var products = _productContext.Products.OrderBy(p => p.Name).AsAsyncEnumerable();

    await foreach (var product in products)
    {
        if (product.IsOnSale)
        {
            yield return product;
        }
    }
}

IActionResult-Typ

Der Rückgabetyp IActionResult eignet sich in Fällen, in denen mehrere ActionResult-Rückgabetypen in einer Aktion möglich sind. Die ActionResult-Typen stellen verschiedene HTTP-Statuscodes dar. Alle nicht abstrakten Klassen, die von ActionResult abgeleitet werden, qualifizieren sich als gültiger Rückgabetyp. Einige allgemeine Rückgabetypen in dieser Kategorie sind BadRequestResult (400), NotFoundResult (404) und OkObjectResult (200). Alternativ können Hilfsmethoden in der ControllerBase-Klasse verwendet werden, um ActionResult-Typen aus einer Aktion zurückzugeben. return BadRequest(); ist beispielsweise eine Kurzform von return new BadRequestResult();.

Da es bei einer Aktion dieser Art mehrere Rückgabetypen und Pfade gibt, sollte das Attribut [ProducesResponseType] großzügig verwendet werden. Dieses Attribut erzeugt aussagekräftigere Antwortdetails für Web-API-Hilfeseiten, die mit Tools wie Swagger erstellt wurden. [ProducesResponseType] gibt die bekannten Typen und HTTP-Statuscodes an, die von der Aktion zurückgegeben werden sollen.

Synchrone Aktion

Bei der folgenden synchronen Aktion gibt es zwei mögliche Rückgabetypen:

[HttpGet("{id}")]
[ProducesResponseType<Product>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetById_IActionResult(int id)
{
    var product = _productContext.Products.Find(id);
    return product == null ? NotFound() : Ok(product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 404-Statuscode wird zurückgegeben, wenn das durch id dargestellte Produkt im zugrunde liegenden Datenspeicher nicht vorhanden ist. Die NotFound-Hilfsmethode wird als Kurzform für return new NotFoundResult(); aufgerufen.
  • Ein 200-Statuscode wird mit dem Product-Objekt zurückgegeben, wenn das Produkt vorhanden ist. Die Ok-Hilfsmethode wird als Kurzform für return new OkObjectResult(product); aufgerufen.

Asynchrone Aktion

Bei der folgenden asynchronen Aktion gibt es zwei mögliche Rückgabetypen:

[HttpPost()]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CreateAsync_IActionResult(Product product)
{
    if (product.Description.Contains("XYZ Widget"))
    {
        return BadRequest();
    }

    _productContext.Products.Add(product);
    await _productContext.SaveChangesAsync();

    return CreatedAtAction(nameof(CreateAsync_IActionResult), new { id = product.Id }, product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 400-Statuscode wird zurückgegeben, wenn die Produktbeschreibung „XYZ-Widget“ enthält. Die BadRequest-Hilfsmethode wird als Kurzform für return new BadRequestResult(); aufgerufen.

  • Ein 201-Statuscode wird von der Hilfsmethode CreatedAtAction generiert, wenn ein Produkt erstellt wird. Der folgende Code stellt eine Alternative zum Aufrufen von CreatedAtAction dar:

    return new CreatedAtActionResult(nameof(CreateAsync), 
                                    "Products", 
                                    new { id = product.Id }, 
                                    product);
    

    In vorstehenden Codepfad wird das Product-Objekt im Antworttext bereitgestellt. Ein Location-Antwortheader, der die neu erstellte Produkt-URL enthält, wird bereitgestellt.

Das folgende Modell gibt beispielsweise an, dass Anforderungen die Name- und Description-Eigenschaften einbinden müssen. Wenn Name und Description in der Anforderung nicht bereitgestellt werden, tritt bei der Modellvalidierung ein Fehler auf.

public class Product
{
    public int Id { get; set; }

    [Required]
    public string Name { get; set; } = string.Empty;

    [Required]
    public string Description { get; set; } = string.Empty;

    public bool IsOnSale { get; set; }
}

Wenn das [ApiController]-Attribut angewendet wird, führen Modellvalidierungsfehler zum Statuscode „400“. Weitere Informationen finden Sie unter Automatische HTTP 400-Antworten.

Vergleich von ActionResult und IActionResult

Im folgenden Abschnitt werden ActionResult und IActionResult verglichen.

ActionResult<T> Typ

ASP.NET Core enthält den Rückgabetyp ActionResult<T>> für Web-API-Controlleraktionen. Damit wird die Rückgabe eines von ActionResult abgeleiteten Typs oder eines bestimmten Typs ermöglicht. ActionResult<T> besitzt gegenüber dem IActionResult-Typ die folgenden Vorteile:

  • Die Type-Eigenschaft des [ProducesResponseType]-Attributs kann ausgeschlossen werden. [ProducesResponseType(200, Type = typeof(Product))] wird beispielsweise zu [ProducesResponseType(200)] vereinfacht. Der erwartete Rückgabetyp der Aktion wird von T in ActionResult<T> abgeleitet.
  • Implizite Umwandlungsoperatoren unterstützen die Konvertierung von T und ActionResult in ActionResult<T>. T wird in ObjectResult konvertiert, wodurch return new ObjectResult(T); zu return T; vereinfacht wird.

C# unterstützt keine impliziten Umwandlungsoperatoren in Schnittstellen. Daher ist die Konvertierung der Schnittstelle in einen konkreten Typ erforderlich, um ActionResult<T> verwenden zu können. Die Verwendung von IEnumerable im folgenden Beispiel funktioniert also nicht:

[HttpGet]
public ActionResult<IEnumerable<Product>> Get() =>
    _repository.GetProducts();

Eine Möglichkeit zur Korrektur des oben stehenden Codes besteht darin, _repository.GetProducts().ToList(); zurückzugeben.

Die meisten Aktionen haben einen bestimmten Rückgabetyp. Während der Ausführung der Aktion können unerwartete Bedingungen auftreten, wodurch der spezifische Typ nicht zurückgegeben wird. So kann beispielsweise die Modellvalidierung des Eingabeparameters einer Aktion fehlschlagen. In diesem Fall wird üblicherweise der entsprechende ActionResult-Typ anstatt des spezifischen Typs zurückgegeben.

Synchrone Aktion

Bei der folgenden synchronen Aktion gibt es zwei mögliche Rückgabetypen:

[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById_ActionResultOfT(int id)
{
    var product = _productContext.Products.Find(id);
    return product == null ? NotFound() : product;
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 404-Statuscode wird zurückgegeben, wenn das Produkt in der Datenbank nicht vorhanden ist.
  • Ein 200-Statuscode wird mit dem entsprechenden Product-Objekt zurückgegeben, wenn das Produkt vorhanden ist.

Asynchrone Aktion

Bei der folgenden asynchronen Aktion gibt es zwei mögliche Rückgabetypen:

[HttpPost()]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<Product>> CreateAsync_ActionResultOfT(Product product)
{
    if (product.Description.Contains("XYZ Widget"))
    {
        return BadRequest();
    }

    _productContext.Products.Add(product);
    await _productContext.SaveChangesAsync();

    return CreatedAtAction(nameof(CreateAsync_ActionResultOfT), new { id = product.Id }, product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 400-Statuscode (BadRequest) wird durch die ASP.NET Core-Runtime zurückgegeben, wenn:
    • Das [ApiController]-Attribut angewendet wurde und bei der Modellvalidierung ein Fehler auftritt.
    • Die Produktbeschreibung „XYZ-Widget“ enthält.
  • Ein 201-Statuscode wird von der Methode CreatedAtAction generiert, wenn ein Produkt erstellt wird. In diesem Codepfad wird das Product-Objekt im Antworttext bereitgestellt. Ein Location-Antwortheader, der die neu erstellte Produkt-URL enthält, wird bereitgestellt.

HttpResults-Typ

Zusätzlich zu den MVC-spezifischen integrierten Ergebnistypen (IActionResult und ActionResult<T>) umfasst ASP.NET Core die HttpResults-Typen, die sowohl in Minimal-APIs als auch in der Web-API verwendet werden können.

Anders als die MVC-spezifischen Ergebnistypen, gilt für HttpResults:

  • Sie sind eine Ergebnisimplementierung, die durch einen Aufruf von IResult.ExecuteAsync verarbeitet wird.

  • Sie nutzen die konfigurierten Formatierernicht. Dass die konfigurierten Formatierer nicht genutzt werden, bedeutet Folgendes:

    • Einige Features wie Content negotiation sind nicht verfügbar.
    • Welcher Content-Type erzeugt wird, wird von der HttpResults-Implementierung entschieden.

HttpResults kann bei der gemeinsamen Nutzung von Code durch Minimal-APIs und die Web-API nützlich sein.

IResult-Typ

Der Microsoft.AspNetCore.Http.HttpResults-Namespace enthält Klassen, die die IResult-Schnittstelle implementieren. Die IResult-Schnittstelle definiert einen Vertrag, der das Ergebnis eines HTTP-Endpunkts darstellt. Die statische Results-Klasse wird verwendet, um unterschiedliche IResult-Objekte zu erstellen, die verschiedene Arten von Antworten darstellen.

Die Tabelle Integrierte Ergebnisse zeigt die allgemeinen Ergebnishilfsprogramme.

Betrachten Sie folgenden Code:

[HttpGet("{id}")]
[ProducesResponseType<Product>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IResult GetById(int id)
{
    var product = _productContext.Products.Find(id);
    return product == null ? Results.NotFound() : Results.Ok(product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 404-Statuscode wird zurückgegeben, wenn das Produkt in der Datenbank nicht vorhanden ist.
  • Ein 200-Statuscode wird mit dem entsprechenden Product-Objekt zurückgegeben, wenn das Produkt vorhanden ist, und wird von Results.Ok<T>() generiert.

Betrachten Sie folgenden Code:

[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType<Product>(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IResult> CreateAsync(Product product)
{
    if (product.Description.Contains("XYZ Widget"))
    {
        return Results.BadRequest();
    }

    _productContext.Products.Add(product);
    await _productContext.SaveChangesAsync();

    var location = Url.Action(nameof(CreateAsync), new { id = product.Id }) ?? $"/{product.Id}";
    return Results.Created(location, product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Der Statuscode 400 wird zurückgegeben, wenn Folgendes zutrifft:
    • Das [ApiController]-Attribut angewendet wurde und bei der Modellvalidierung ein Fehler auftritt.
    • Die Produktbeschreibung „XYZ-Widget“ enthält.
  • Ein 201-Statuscode wird von der Methode Results.Create generiert, wenn ein Produkt erstellt wird. In diesem Codepfad wird das Product-Objekt im Antworttext bereitgestellt. Ein Location-Antwortheader, der die neu erstellte Produkt-URL enthält, wird bereitgestellt.

Ergebnisse<TResult1-, TResultN>-Typ

Die statische TypedResults-Klasse gibt die konkrete IResult-Implementierung zurück, die die Verwendung von IResult als Rückgabetyp ermöglicht. Die Verwendung der konkreten IResult-Implementierung bietet gegenüber dem IResult-Typ folgenden Vorteil:

  • Alle Attribute von [ProducesResponseType] können ausgeschlossen werden, da die HttpResultImplementierung automatisch zu den Endpunktmetadaten beiträgt.

Wenn mehrere IResult-Rückgabetypen erforderlich sind, ist die Rückgabe von Results<TResult1, TResultN> der Rückgabe von IResult vorzuziehen. Die Rückgabe von Results<TResult1, TResultN> ist zu bevorzugen, weil die generischen Union-Typen automatisch die Metadaten des Endpunkts beibehalten.

Die Union-Typen von Results<TResult1, TResultN> implementieren implizite Umwandlungsoperatoren, damit der Compiler die in den generischen Argumenten angegebenen Typen automatisch in eine Instanz des Union-Typs konvertieren kann. Dies hat den zusätzlichen Vorteil, dass zur Kompilierungszeit überprüft wird, ob ein Routenhandler tatsächlich nur die Ergebnisse zurückgibt, die er deklariert. Der Versuch, einen Typ zurückzugeben, der nicht als eines der generischen Argumente für Results<> deklariert wurde, führt zu einem Kompilierungsfehler.

Betrachten Sie folgenden Code:

[HttpGet("{id}")]
public Results<NotFound, Ok<Product>> GetById(int id)
{
    var product = _productContext.Products.Find(id);
    return product == null ? TypedResults.NotFound() : TypedResults.Ok(product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 404-Statuscode wird zurückgegeben, wenn das Produkt in der Datenbank nicht vorhanden ist.
  • Ein 200-Statuscode wird mit dem entsprechenden Product-Objekt zurückgegeben, wenn das Produkt vorhanden ist, und wird von TypedResults.Ok<T> generiert.
[HttpPost]
public async Task<Results<BadRequest, Created<Product>>> CreateAsync(Product product)
{
    if (product.Description.Contains("XYZ Widget"))
    {
        return TypedResults.BadRequest();
    }

    _productContext.Products.Add(product);
    await _productContext.SaveChangesAsync();

    var location = Url.Action(nameof(CreateAsync), new { id = product.Id }) ?? $"/{product.Id}";
    return TypedResults.Created(location, product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Der Statuscode 400 wird zurückgegeben, wenn Folgendes zutrifft:
    • Das [ApiController]-Attribut wurde angewendet, und bei der Modellvalidierung tritt ein Fehler auf.
    • Die Produktbeschreibung „XYZ-Widget“ enthält.
  • Ein 201-Statuscode wird von der Methode TypedResults.Created generiert, wenn ein Produkt erstellt wird. In diesem Codepfad wird das Product-Objekt im Antworttext bereitgestellt. Ein Location-Antwortheader, der die neu erstellte Produkt-URL enthält, wird bereitgestellt.

Zusätzliche Ressourcen

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)

ASP.NET Core bietet die folgenden Optionen für Rückgabetypen für Web-API-Controlleraktionen:

In diesem Artikel wird erläutert, wann die einzelnen Rückgabetypen am sinnvollsten zu verwenden sind.

Spezifischer Typ

Die einfachste Aktion gibt einen primitiven oder komplexen Datentyp zurück (z. B. string oder einen benutzerdefinierten Objekttyp). Die folgende Aktion gibt eine Auflistung benutzerdefinierter Product-Objekte zurück:

[HttpGet]
public Task<List<Product>> Get() =>
    _productContext.Products.OrderBy(p => p.Name).ToListAsync();

Ohne bekannte Bedingungen, gegen die während Schutzmaßnahmen ergriffen werden müssen, reicht möglicherweise die Rückgabe eines bestimmten Typs aus. Die vorherige Aktion akzeptiert keine Parameter, weshalb eine Validierung von Parametereinschränkungen nicht erforderlich ist.

Wenn mehrere Rückgabetypen möglich sind, ist es üblich, einen ActionResult-Rückgabetyp mit dem primitiven oder komplexen Rückgabetyp zu mischen. Für diese Art von Aktion sind entweder IActionResult oder ActionResult<T> erforderlich. Dieser Artikel enthält einige Beispiele für mehrere Rückgabetypen.

Rückgabe von IEnumerable<T> oder IAsyncEnumerable<T>

Überlegungen zur Leistung finden Sie unter Rückgabe von IEnumerable<T> oder IAsyncEnumerable<T>.

ASP.NET Core puffert das Ergebnis von Aktionen, die IEnumerable<T> zurückgeben, bevor sie diese in die Antwort schreiben. Sie sollten den Rückgabetyp der Signatur der Aktion als IAsyncEnumerable<T> deklarieren, um eine asynchrone Iteration sicherzustellen. Letztendlich basiert der Iterationsmodus auf dem zugrunde liegenden konkreten Typ, der zurückgegeben wird, und der ausgewählte Formatierer beeinflusst die Verarbeitung des Ergebnisses:

  • Bei Verwendung des System.Text.Json-Formatierers verlässt sich MVC auf die Unterstützung, die System.Text.Json zum Streamen des Ergebnisses hinzugefügt hat.
  • Bei Verwendung von Newtonsoft.Json oder mit XML-based-Formatierern wird das Ergebnis gepuffert.

Sehen Sie sich die folgende Aktion an, die den Verkaufspreis enthaltende Produktdatensätze als IEnumerable<Product> zurückgibt:

[HttpGet("syncsale")]
public IEnumerable<Product> GetOnSaleProducts()
{
    var products = _productContext.Products.OrderBy(p => p.Name).ToList();

    foreach (var product in products)
    {
        if (product.IsOnSale)
        {
            yield return product;
        }
    }
}

Die IAsyncEnumerable<Product>-Entsprechung der vorherigen Aktion lautet wie folgt:

[HttpGet("asyncsale")]
public async IAsyncEnumerable<Product> GetOnSaleProductsAsync()
{
    var products = _productContext.Products.OrderBy(p => p.Name).AsAsyncEnumerable();

    await foreach (var product in products)
    {
        if (product.IsOnSale)
        {
            yield return product;
        }
    }
}

IActionResult-Typ

Der Rückgabetyp IActionResult eignet sich in Fällen, in denen mehrere ActionResult-Rückgabetypen in einer Aktion möglich sind. Die ActionResult-Typen stellen verschiedene HTTP-Statuscodes dar. Alle nicht abstrakten Klassen, die von ActionResult abgeleitet werden, qualifizieren sich als gültiger Rückgabetyp. Einige allgemeine Rückgabetypen in dieser Kategorie sind BadRequestResult (400), NotFoundResult (404) und OkObjectResult (200). Alternativ können Hilfsmethoden in der ControllerBase-Klasse verwendet werden, um ActionResult-Typen aus einer Aktion zurückzugeben. return BadRequest(); ist beispielsweise eine Kurzform von return new BadRequestResult();.

Da es bei einer Aktion dieser Art mehrere Rückgabetypen und Pfade gibt, sollte das Attribut [ProducesResponseType] großzügig verwendet werden. Dieses Attribut erzeugt aussagekräftigere Antwortdetails für Web-API-Hilfeseiten, die mit Tools wie Swagger erstellt wurden. [ProducesResponseType] gibt die bekannten Typen und HTTP-Statuscodes an, die von der Aktion zurückgegeben werden sollen.

Synchrone Aktion

Bei der folgenden synchronen Aktion gibt es zwei mögliche Rückgabetypen:

[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Product))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetById_IActionResult(int id)
{
    var product = _productContext.Products.Find(id);
    return product == null ? NotFound() : Ok(product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 404-Statuscode wird zurückgegeben, wenn das durch id dargestellte Produkt im zugrunde liegenden Datenspeicher nicht vorhanden ist. Die NotFound-Hilfsmethode wird als Kurzform für return new NotFoundResult(); aufgerufen.
  • Ein 200-Statuscode wird mit dem Product-Objekt zurückgegeben, wenn das Produkt vorhanden ist. Die Ok-Hilfsmethode wird als Kurzform für return new OkObjectResult(product); aufgerufen.

Asynchrone Aktion

Bei der folgenden asynchronen Aktion gibt es zwei mögliche Rückgabetypen:

[HttpPost()]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CreateAsync_IActionResult(Product product)
{
    if (product.Description.Contains("XYZ Widget"))
    {
        return BadRequest();
    }

    _productContext.Products.Add(product);
    await _productContext.SaveChangesAsync();

    return CreatedAtAction(nameof(GetById_IActionResult), new { id = product.Id }, product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 400-Statuscode wird zurückgegeben, wenn die Produktbeschreibung „XYZ-Widget“ enthält. Die BadRequest-Hilfsmethode wird als Kurzform für return new BadRequestResult(); aufgerufen.

  • Ein 201-Statuscode wird von der Hilfsmethode CreatedAtAction generiert, wenn ein Produkt erstellt wird. Der folgende Code stellt eine Alternative zum Aufrufen von CreatedAtAction dar:

    return new CreatedAtActionResult(nameof(GetById), 
                                    "Products", 
                                    new { id = product.Id }, 
                                    product);
    

    In vorstehenden Codepfad wird das Product-Objekt im Antworttext bereitgestellt. Ein Location-Antwortheader, der die neu erstellte Produkt-URL enthält, wird bereitgestellt.

Das folgende Modell gibt beispielsweise an, dass Anforderungen die Name- und Description-Eigenschaften einbinden müssen. Wenn Name und Description in der Anforderung nicht bereitgestellt werden, tritt bei der Modellvalidierung ein Fehler auf.

public class Product
{
    public int Id { get; set; }

    [Required]
    public string Name { get; set; } = string.Empty;

    [Required]
    public string Description { get; set; } = string.Empty;

    public bool IsOnSale { get; set; }
}

Wenn das [ApiController]-Attribut angewendet wird, führen Modellvalidierungsfehler zum Statuscode „400“. Weitere Informationen finden Sie unter Automatische HTTP 400-Antworten.

Vergleich von ActionResult und IActionResult

Im folgenden Abschnitt werden ActionResult und IActionResult verglichen.

ActionResult<T> Typ

ASP.NET Core enthält den Rückgabetyp ActionResult<T>> für Web-API-Controlleraktionen. Damit wird die Rückgabe eines von ActionResult abgeleiteten Typs oder eines bestimmten Typs ermöglicht. ActionResult<T> besitzt gegenüber dem IActionResult-Typ die folgenden Vorteile:

  • Die Type-Eigenschaft des [ProducesResponseType]-Attributs kann ausgeschlossen werden. [ProducesResponseType(200, Type = typeof(Product))] wird beispielsweise zu [ProducesResponseType(200)] vereinfacht. Der erwartete Rückgabetyp der Aktion wird von T in ActionResult<T> abgeleitet.
  • Implizite Umwandlungsoperatoren unterstützen die Konvertierung von T und ActionResult in ActionResult<T>. T wird in ObjectResult konvertiert, wodurch return new ObjectResult(T); zu return T; vereinfacht wird.

C# unterstützt keine impliziten Umwandlungsoperatoren in Schnittstellen. Daher ist die Konvertierung der Schnittstelle in einen konkreten Typ erforderlich, um ActionResult<T> verwenden zu können. Die Verwendung von IEnumerable im folgenden Beispiel funktioniert also nicht:

[HttpGet]
public ActionResult<IEnumerable<Product>> Get() =>
    _repository.GetProducts();

Eine Möglichkeit zur Korrektur des oben stehenden Codes besteht darin, _repository.GetProducts().ToList(); zurückzugeben.

Die meisten Aktionen haben einen bestimmten Rückgabetyp. Während der Ausführung der Aktion können unerwartete Bedingungen auftreten, wodurch der spezifische Typ nicht zurückgegeben wird. So kann beispielsweise die Modellvalidierung des Eingabeparameters einer Aktion fehlschlagen. In diesem Fall wird üblicherweise der entsprechende ActionResult-Typ anstatt des spezifischen Typs zurückgegeben.

Synchrone Aktion

Bei der folgenden synchronen Aktion gibt es zwei mögliche Rückgabetypen:

[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById_ActionResultOfT(int id)
{
    var product = _productContext.Products.Find(id);
    return product == null ? NotFound() : product;
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 404-Statuscode wird zurückgegeben, wenn das Produkt in der Datenbank nicht vorhanden ist.
  • Ein 200-Statuscode wird mit dem entsprechenden Product-Objekt zurückgegeben, wenn das Produkt vorhanden ist.

Asynchrone Aktion

Bei der folgenden asynchronen Aktion gibt es zwei mögliche Rückgabetypen:

[HttpPost()]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<Product>> CreateAsync_ActionResultOfT(Product product)
{
    if (product.Description.Contains("XYZ Widget"))
    {
        return BadRequest();
    }

    _productContext.Products.Add(product);
    await _productContext.SaveChangesAsync();

    return CreatedAtAction(nameof(GetById_ActionResultOfT), new { id = product.Id }, product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 400-Statuscode (BadRequest) wird durch die ASP.NET Core-Runtime zurückgegeben, wenn:
    • Das [ApiController]-Attribut angewendet wurde und bei der Modellvalidierung ein Fehler auftritt.
    • Die Produktbeschreibung „XYZ-Widget“ enthält.
  • Ein 201-Statuscode wird von der Methode CreatedAtAction generiert, wenn ein Produkt erstellt wird. In diesem Codepfad wird das Product-Objekt im Antworttext bereitgestellt. Ein Location-Antwortheader, der die neu erstellte Produkt-URL enthält, wird bereitgestellt.

HttpResults-Typ

Zusätzlich zu den MVC-spezifischen integrierten Ergebnistypen (IActionResult und ActionResult<T>) umfasst ASP.NET Core die HttpResults-Typen, die sowohl in Minimal-APIs als auch in der Web-API verwendet werden können.

Anders als die MVC-spezifischen Ergebnistypen, gilt für HttpResults:

  • Sie sind eine Ergebnisimplementierung, die durch einen Aufruf von IResult.ExecuteAsync verarbeitet wird.

  • Sie nutzen die konfigurierten Formatierernicht. Dass die konfigurierten Formatierer nicht genutzt werden, bedeutet Folgendes:

    • Einige Features wie Content negotiation sind nicht verfügbar.
    • Welcher Content-Type erzeugt wird, wird von der HttpResults-Implementierung entschieden.

HttpResults kann bei der gemeinsamen Nutzung von Code durch Minimal-APIs und die Web-API nützlich sein.

IResult-Typ

Der Microsoft.AspNetCore.Http.HttpResults-Namespace enthält Klassen, die die IResult-Schnittstelle implementieren. Die IResult-Schnittstelle definiert einen Vertrag, der das Ergebnis eines HTTP-Endpunkts darstellt. Die statische Results-Klasse wird verwendet, um unterschiedliche IResult-Objekte zu erstellen, die verschiedene Arten von Antworten darstellen.

Die Tabelle Integrierte Ergebnisse zeigt die allgemeinen Ergebnishilfsprogramme.

Betrachten Sie folgenden Code:

[HttpGet("{id}")]
[ProducesResponseType(typeof(Product), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IResult GetById(int id)
{
    var product = _productContext.Products.Find(id);
    return product == null ? Results.NotFound() : Results.Ok(product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 404-Statuscode wird zurückgegeben, wenn das Produkt in der Datenbank nicht vorhanden ist.
  • Ein 200-Statuscode wird mit dem entsprechenden Product-Objekt zurückgegeben, wenn das Produkt vorhanden ist, und wird von Results.Ok<T>() generiert.

Betrachten Sie folgenden Code:

[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(typeof(Product), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IResult> CreateAsync(Product product)
{
    if (product.Description.Contains("XYZ Widget"))
    {
        return Results.BadRequest();
    }

    _productContext.Products.Add(product);
    await _productContext.SaveChangesAsync();

    var location = Url.Action(nameof(GetById), new { id = product.Id }) ?? $"/{product.Id}";
    return Results.Created(location, product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Der Statuscode 400 wird zurückgegeben, wenn Folgendes zutrifft:
    • Das [ApiController]-Attribut angewendet wurde und bei der Modellvalidierung ein Fehler auftritt.
    • Die Produktbeschreibung „XYZ-Widget“ enthält.
  • Ein 201-Statuscode wird von der Methode Results.Create generiert, wenn ein Produkt erstellt wird. In diesem Codepfad wird das Product-Objekt im Antworttext bereitgestellt. Ein Location-Antwortheader, der die neu erstellte Produkt-URL enthält, wird bereitgestellt.

Ergebnisse<TResult1-, TResultN>-Typ

Die statische TypedResults-Klasse gibt die konkrete IResult-Implementierung zurück, die die Verwendung von IResult als Rückgabetyp ermöglicht. Die Verwendung der konkreten IResult-Implementierung bietet gegenüber dem IResult-Typ folgenden Vorteil:

  • Alle Attribute von [ProducesResponseType] können ausgeschlossen werden, da die HttpResultImplementierung automatisch zu den Endpunktmetadaten beiträgt.

Wenn mehrere IResult-Rückgabetypen erforderlich sind, ist die Rückgabe von Results<TResult1, TResultN> der Rückgabe von IResult vorzuziehen. Die Rückgabe von Results<TResult1, TResultN> ist zu bevorzugen, weil die generischen Union-Typen automatisch die Metadaten des Endpunkts beibehalten.

Die Union-Typen von Results<TResult1, TResultN> implementieren implizite Umwandlungsoperatoren, damit der Compiler die in den generischen Argumenten angegebenen Typen automatisch in eine Instanz des Union-Typs konvertieren kann. Dies hat den zusätzlichen Vorteil, dass zur Kompilierungszeit überprüft wird, ob ein Routenhandler tatsächlich nur die Ergebnisse zurückgibt, die er deklariert. Der Versuch, einen Typ zurückzugeben, der nicht als eines der generischen Argumente für Results<> deklariert wurde, führt zu einem Kompilierungsfehler.

Betrachten Sie folgenden Code:

[HttpGet("{id}")]
public Results<NotFound, Ok<Product>> GetById(int id)
{
    var product = _productContext.Products.Find(id);
    return product == null ? TypedResults.NotFound() : TypedResults.Ok(product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 404-Statuscode wird zurückgegeben, wenn das Produkt in der Datenbank nicht vorhanden ist.
  • Ein 200-Statuscode wird mit dem entsprechenden Product-Objekt zurückgegeben, wenn das Produkt vorhanden ist, und wird von TypedResults.Ok<T> generiert.
[HttpPost]
public async Task<Results<BadRequest, Created<Product>>> CreateAsync(Product product)
{
    if (product.Description.Contains("XYZ Widget"))
    {
        return TypedResults.BadRequest();
    }

    _productContext.Products.Add(product);
    await _productContext.SaveChangesAsync();

    var location = Url.Action(nameof(GetById), new { id = product.Id }) ?? $"/{product.Id}";
    return TypedResults.Created(location, product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Der Statuscode 400 wird zurückgegeben, wenn Folgendes zutrifft:
    • Das [ApiController]-Attribut wurde angewendet, und bei der Modellvalidierung tritt ein Fehler auf.
    • Die Produktbeschreibung „XYZ-Widget“ enthält.
  • Ein 201-Statuscode wird von der Methode TypedResults.Create generiert, wenn ein Produkt erstellt wird. In diesem Codepfad wird das Product-Objekt im Antworttext bereitgestellt. Ein Location-Antwortheader, der die neu erstellte Produkt-URL enthält, wird bereitgestellt.

Zusätzliche Ressourcen

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)

In ASP.NET Core haben Sie die folgenden Optionen für Rückgabetypen für Web-API-Controlleraktionen:

In diesem Artikel wird die Verwendung der einzelnen Rückgabetypen erklärt.

Spezifischer Typ

Die einfachste Aktion gibt einen primitiven oder komplexen Datentyp zurück (z.B. string oder einen benutzerdefinierten Objekttyp). Die folgende Aktion gibt eine Auflistung benutzerdefinierter Product-Objekte zurück:

[HttpGet]
public List<Product> Get() =>
    _repository.GetProducts();

Ohne bekannte Bedingungen, gegen die während der Ausführung einer Aktion Schutzmaßnahmen ergriffen werden müssen, reicht die Rückgabe eines spezifischen Typs möglicherweise aus. Die vorherige Aktion akzeptiert keine Parameter, weshalb eine Validierung von Parametereinschränkungen nicht erforderlich ist.

Wenn mehrere Rückgabetypen möglich sind, ist es üblich, einen ActionResult-Rückgabetyp mit dem primitiven oder komplexen Rückgabetyp zu mischen. Für diese Art von Aktion sind entweder IActionResult oder ActionResult<T> erforderlich. Dieses Dokument enthält einige Beispiele für mehrere Rückgabetypen.

Rückgabe von IEnumerable<T> oder IAsyncEnumerable<T>

ASP.NET Core puffert das Ergebnis von Aktionen, die IEnumerable<T> zurückgeben, bevor sie diese in die Antwort schreiben. Sie sollten den Rückgabetyp der Signatur der Aktion als IAsyncEnumerable<T> deklarieren, um eine asynchrone Iteration sicherzustellen. Letztendlich basiert der Iterationsmodus auf dem zugrunde liegenden konkreten Typ, der zurückgegeben wird. MVC puffert automatisch jeden konkreten Typ, der IAsyncEnumerable<T> implementiert.

Sehen Sie sich die folgende Aktion an, die den Verkaufspreis enthaltende Produktdatensätze als IEnumerable<Product> zurückgibt:

[HttpGet("syncsale")]
public IEnumerable<Product> GetOnSaleProducts()
{
    var products = _repository.GetProducts();

    foreach (var product in products)
    {
        if (product.IsOnSale)
        {
            yield return product;
        }
    }
}

Die IAsyncEnumerable<Product>-Entsprechung der vorherigen Aktion lautet wie folgt:

[HttpGet("asyncsale")]
public async IAsyncEnumerable<Product> GetOnSaleProductsAsync()
{
    var products = _repository.GetProductsAsync();

    await foreach (var product in products)
    {
        if (product.IsOnSale)
        {
            yield return product;
        }
    }
}

IActionResult-Typ

Der Rückgabetyp IActionResult eignet sich in Fällen, in denen mehrere ActionResult-Rückgabetypen in einer Aktion möglich sind. Die ActionResult-Typen stellen verschiedene HTTP-Statuscodes dar. Alle nicht abstrakten Klassen, die von ActionResult abgeleitet werden, qualifizieren sich als gültiger Rückgabetyp. Einige allgemeine Rückgabetypen in dieser Kategorie sind BadRequestResult (400), NotFoundResult (404) und OkObjectResult (200). Alternativ können Hilfsmethoden in der ControllerBase-Klasse verwendet werden, um ActionResult-Typen aus einer Aktion zurückzugeben. return BadRequest(); ist beispielsweise eine Kurzform von return new BadRequestResult();.

Da es bei einer Aktion dieser Art mehrere Rückgabetypen und Pfade gibt, sollte das Attribut [ProducesResponseType] großzügig verwendet werden. Dieses Attribut erzeugt aussagekräftigere Antwortdetails für Web-API-Hilfeseiten, die mit Tools wie Swagger erstellt wurden. [ProducesResponseType] gibt die bekannten Typen und HTTP-Statuscodes an, die von der Aktion zurückgegeben werden sollen.

Synchrone Aktion

Bei der folgenden synchronen Aktion gibt es zwei mögliche Rückgabetypen:

[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Product))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetById(int id)
{
    if (!_repository.TryGetProduct(id, out var product))
    {
        return NotFound();
    }

    return Ok(product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 404-Statuscode wird zurückgegeben, wenn das durch id dargestellte Produkt im zugrunde liegenden Datenspeicher nicht vorhanden ist. Die NotFound-Hilfsmethode wird als Kurzform für return new NotFoundResult(); aufgerufen.
  • Ein 200-Statuscode wird mit dem Product-Objekt zurückgegeben, wenn das Produkt vorhanden ist. Die Ok-Hilfsmethode wird als Kurzform für return new OkObjectResult(product); aufgerufen.

Asynchrone Aktion

Bei der folgenden asynchronen Aktion gibt es zwei mögliche Rückgabetypen:

[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CreateAsync(Product product)
{
    if (product.Description.Contains("XYZ Widget"))
    {
        return BadRequest();
    }

    await _repository.AddProductAsync(product);

    return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 400-Statuscode wird zurückgegeben, wenn die Produktbeschreibung „XYZ-Widget“ enthält. Die BadRequest-Hilfsmethode wird als Kurzform für return new BadRequestResult(); aufgerufen.
  • Ein 201-Statuscode wird von der Hilfsmethode CreatedAtAction generiert, wenn ein Produkt erstellt wird. Eine Alternative zum Aufrufen von CreatedAtAction ist return new CreatedAtActionResult(nameof(GetById), "Products", new { id = product.Id }, product);. In diesem Codepfad wird das Product-Objekt im Antworttext bereitgestellt. Ein Location-Antwortheader, der die neu erstellte Produkt-URL enthält, wird bereitgestellt.

Das folgende Modell gibt beispielsweise an, dass Anforderungen die Name- und Description-Eigenschaften einbinden müssen. Wenn Name und Description in der Anforderung nicht bereitgestellt werden, tritt bei der Modellvalidierung ein Fehler auf.

public class Product
{
    public int Id { get; set; }

    [Required]
    public string Name { get; set; }

    [Required]
    public string Description { get; set; }

    public bool IsOnSale { get; set; }
}

Wenn das [ApiController]-Attribut angewendet wird, führen Modellvalidierungsfehler zum Statuscode „400“. Weitere Informationen finden Sie unter Automatische HTTP 400-Antworten.

Vergleich von ActionResult und IActionResult

Im folgenden Abschnitt werden ActionResult und IActionResult verglichen.

ActionResult<T> Typ

ASP.NET Core enthält den Rückgabetyp ActionResult<T>> für Web-API-Controlleraktionen. Damit wird die Rückgabe eines von ActionResult abgeleiteten Typs oder eines spezifischen Typs ermöglicht. ActionResult<T> besitzt gegenüber dem IActionResult-Typ die folgenden Vorteile:

  • Die Type-Eigenschaft des [ProducesResponseType]-Attributs kann ausgeschlossen werden. [ProducesResponseType(200, Type = typeof(Product))] wird beispielsweise zu [ProducesResponseType(200)] vereinfacht. Der erwartete Rückgabetyp der Aktion wird stattdessen von T in ActionResult<T> abgeleitet.
  • Implizite Umwandlungsoperatoren unterstützen die Konvertierung von T und ActionResult in ActionResult<T>. T wird in ObjectResult konvertiert, wodurch return new ObjectResult(T); zu return T; vereinfacht wird.

C# unterstützt keine impliziten Umwandlungsoperatoren in Schnittstellen. Daher ist die Konvertierung der Schnittstelle in einen konkreten Typ erforderlich, um ActionResult<T> verwenden zu können. Die Verwendung von IEnumerable im folgenden Beispiel funktioniert also nicht:

[HttpGet]
public ActionResult<IEnumerable<Product>> Get() =>
    _repository.GetProducts();

Eine Möglichkeit zur Korrektur des oben stehenden Codes besteht darin, _repository.GetProducts().ToList(); zurückzugeben.

Die meisten Aktionen haben einen bestimmten Rückgabetyp. Während der Ausführung der Aktion können unerwartete Bedingungen auftreten, wodurch der spezifische Typ nicht zurückgegeben wird. So kann beispielsweise die Modellvalidierung des Eingabeparameters einer Aktion fehlschlagen. In diesem Fall wird üblicherweise der entsprechende ActionResult-Typ anstatt des spezifischen Typs zurückgegeben.

Synchrone Aktion

Bei der folgenden synchronen Aktion gibt es zwei mögliche Rückgabetypen:

[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById(int id)
{
    if (!_repository.TryGetProduct(id, out var product))
    {
        return NotFound();
    }

    return product;
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 404-Statuscode wird zurückgegeben, wenn das Produkt in der Datenbank nicht vorhanden ist.
  • Ein 200-Statuscode wird mit dem entsprechenden Product-Objekt zurückgegeben, wenn das Produkt vorhanden ist.

Asynchrone Aktion

Bei der folgenden asynchronen Aktion gibt es zwei mögliche Rückgabetypen:

[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<Product>> CreateAsync(Product product)
{
    if (product.Description.Contains("XYZ Widget"))
    {
        return BadRequest();
    }

    await _repository.AddProductAsync(product);

    return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}

In der vorhergehenden Aktion geschieht Folgendes:

  • Ein 400-Statuscode (BadRequest) wird durch die ASP.NET Core-Runtime zurückgegeben, wenn:
    • Das [ApiController]-Attribut angewendet wurde und bei der Modellvalidierung ein Fehler auftritt.
    • Die Produktbeschreibung „XYZ-Widget“ enthält.
  • Ein 201-Statuscode wird von der Methode CreatedAtAction generiert, wenn ein Produkt erstellt wird. In diesem Codepfad wird das Product-Objekt im Antworttext bereitgestellt. Ein Location-Antwortheader, der die neu erstellte Produkt-URL enthält, wird bereitgestellt.

Zusätzliche Ressourcen