Zwracane typy akcji kontrolera w internetowym interfejsie API platformy ASP.NET Core

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

Ważne

Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.

Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

Wyświetl lub pobierz przykładowy kod (jak pobrać)

ASP.NET Core udostępnia następujące opcje dla typów zwracanych akcji kontrolera internetowego interfejsu API:

W tym artykule wyjaśniono, kiedy najbardziej odpowiednie jest użycie każdego typu zwracanego.

Określony typ

Najbardziej podstawowa akcja zwraca typ danych pierwotnych lub złożonych, na przykład string lub obiekt niestandardowy. Rozważmy następującą akcję, która zwraca kolekcję obiektów niestandardowych Product :

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

Bez znanych warunków ochrony przed zwróceniem określonego typu może wystarczyć. Poprzednia akcja nie akceptuje żadnych parametrów, więc weryfikacja ograniczeń parametrów nie jest wymagana.

Jeśli jest możliwe wiele typów zwracanych, często należy mieszać typ zwracany z typem ActionResult zwracanym typu pierwotnego lub złożonego. Aby uwzględnić ten typ akcji, konieczne jest rozwiązanie IActionResult lub ActionResult<T> . W tym artykule przedstawiono kilka przykładów wielu typów zwracanych.

Zwracanie wartości IEnumerable<T> lub IAsyncEnumerable<T>

Zobacz Return or for performance considerations (Zwracanie IEnumerable<T> lub IAsyncEnumerable<T> zagadnienia dotyczące wydajności).

ASP.NET Core buforuje wynik akcji, które zwracają IEnumerable<T> się przed zapisaniem ich w odpowiedzi. Rozważ zadeklarowanie typu zwrotnego podpisu akcji w IAsyncEnumerable<T> celu zagwarantowania asynchronicznej iteracji. Ostatecznie tryb iteracji jest oparty na zwracanym podstawowym typie betonowym, a wybrany formater wpływa na sposób przetwarzania wyniku:

  • W przypadku korzystania z System.Text.Json programu formatującego mvC opiera się na obsłudze System.Text.Json dodanej w celu przesyłania strumieniowego wyniku.
  • W przypadku używania Newtonsoft.Json lub z formaterami XML-based wynik jest buforowany.

Rozważmy następującą akcję, która zwraca rekordy produktów w cenie sprzedaży jako IEnumerable<Product>:

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

Odpowiednikiem IAsyncEnumerable<Product> poprzedniej akcji jest:

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

Typ IActionResult

Zwracany IActionResult typ jest odpowiedni, gdy w akcji można wykonać wiele ActionResult zwracanych typów. Typy ActionResult reprezentują różne kody stanu HTTP. Każda nie abstrakcyjna klasa wyprowadzająca z ActionResult kwalifikuje się jako prawidłowy typ zwracany. Niektóre typowe typy zwracane w tej kategorii to BadRequestResult (400), NotFoundResult (404) i OkObjectResult (200). Alternatywnie metody wygodne w ControllerBase klasie mogą służyć do zwracania ActionResult typów z akcji. Na przykład return BadRequest(); jest skróconą formą return new BadRequestResult();.

Ponieważ istnieje wiele typów zwracanych i ścieżek w tym typie akcji, konieczne jest liberalne użycie atrybutu [ProducesResponseType] . Ten atrybut tworzy bardziej opisowe szczegóły odpowiedzi dla stron pomocy interfejsu API sieci Web generowanych przez narzędzia, takie jak Swagger. [ProducesResponseType] wskazuje znane typy i kody stanu HTTP, które mają być zwracane przez akcję.

Akcja synchroniczna

Rozważmy następującą akcję synchroniczną, w której istnieją dwa możliwe typy zwracane:

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

W poprzedniej akcji:

  • Kod stanu 404 jest zwracany, gdy produkt reprezentowany przez id nie istnieje w bazowym magazynie danych. Metoda wygody NotFound jest wywoływana jako skrócona dla .return new NotFoundResult();
  • Kod stanu 200 jest zwracany z obiektem Product , gdy produkt istnieje. Metoda wygody Ok jest wywoływana jako skrócona dla .return new OkObjectResult(product);

Akcja asynchroniczna

Rozważ następującą akcję asynchroniczną, w której istnieją dwa możliwe typy zwracane:

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

W poprzedniej akcji:

  • Kod stanu 400 jest zwracany, gdy opis produktu zawiera element "Widżet XYZ". Metoda wygody BadRequest jest wywoływana jako skrócona dla .return new BadRequestResult();

  • Kod stanu 201 jest generowany przez CreatedAtAction metodę wygody podczas tworzenia produktu. Poniższy kod jest alternatywą dla wywołania metody CreatedAtAction:

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

    W poprzedniej ścieżce Product kodu obiekt jest udostępniany w treści odpowiedzi. Zostanie Location udostępniony nagłówek odpowiedzi zawierający adres URL nowo utworzonego produktu.

Na przykład poniższy model wskazuje, że żądania muszą zawierać Name właściwości i Description . Niepowodzenie podania Name i Description w żądaniu powoduje niepowodzenie walidacji modelu.

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

[ApiController] Jeśli atrybut zostanie zastosowany, błędy walidacji modelu spowodują wyświetlenie kodu stanu 400. Aby uzyskać więcej informacji, zobacz Automatyczne odpowiedzi HTTP 400.

ActionResult vs IActionResult

Poniższa sekcja jest porównywana ActionResult z IActionResult

ActionResult T<>, typ

ASP.NET Core zawiera typ zwracany ActionResult<T> dla akcji internetowego kontrolera interfejsu API. Umożliwia zwracanie typu pochodzącego z ActionResult lub zwracanie określonego typu. ActionResult<T> oferuje następujące korzyści wynikające z typu IActionResult:

  • [ProducesResponseType] Właściwość atrybutu Type można wykluczyć. Na przykład [ProducesResponseType(200, Type = typeof(Product))] jest uproszczony do [ProducesResponseType(200)]. Oczekiwany typ zwracany akcji jest wywnioskowany z elementu T w ActionResult<T>pliku .
  • Niejawne operatory rzutowania obsługują konwersję elementów i TActionResult na ActionResult<T>. T konwertuje na ObjectResultwartość , co oznacza return new ObjectResult(T); , że jest uproszczone na return T;.

Język C# nie obsługuje niejawnych operatorów rzutowania w interfejsach. W związku z tym konwersja interfejsu na konkretny typ jest niezbędna do użycia elementu ActionResult<T>. Na przykład użycie elementu IEnumerable w poniższym przykładzie nie działa:

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

Jedną z opcji naprawienia poprzedniego kodu jest zwrócenie _repository.GetProducts().ToList();polecenia .

Większość akcji ma określony typ zwracany. Podczas wykonywania akcji mogą wystąpić nieoczekiwane warunki, w tym przypadku określony typ nie jest zwracany. Na przykład parametr wejściowy akcji może zakończyć się niepowodzeniem weryfikacji modelu. W takim przypadku często zwracany jest odpowiedni ActionResult typ zamiast określonego typu.

Akcja synchroniczna

Rozważ akcję synchroniczną, w której istnieją dwa możliwe typy zwracane:

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

W poprzedniej akcji:

  • Kod stanu 404 jest zwracany, gdy produkt nie istnieje w bazie danych.
  • Kod stanu 200 jest zwracany z odpowiednim Product obiektem, gdy produkt istnieje.

Akcja asynchroniczna

Rozważ akcję asynchroniczną, w której istnieją dwa możliwe typy zwracane:

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

W poprzedniej akcji:

  • Kod stanu 400 (BadRequest) jest zwracany przez środowisko uruchomieniowe ASP.NET Core, gdy:
    • Atrybut został zastosowany, a walidacja [ApiController] modelu kończy się niepowodzeniem.
    • Opis produktu zawiera element "Widżet XYZ".
  • Kod stanu 201 jest generowany przez CreatedAtAction metodę podczas tworzenia produktu. W tej ścieżce Product kodu obiekt jest udostępniany w treści odpowiedzi. Zostanie Location udostępniony nagłówek odpowiedzi zawierający adres URL nowo utworzonego produktu.

Typ HttpResults

Oprócz wbudowanych typów wyników mvC (IActionResult i ActionResult<T>) ASP.NET Core obejmuje typy HttpResults , które mogą być używane zarówno w minimalnych interfejsach API, jak i w internetowym interfejsie API.

Inne niż typy wyników specyficzne dla wzorca MVC:HttpResults

  • To implementacja wyników przetwarzana przez wywołanie metody IResult.ExecuteAsync.

  • Nie korzysta ze skonfigurowanych formatów. Nieuwzmaganie skonfigurowanych formaterów oznacza:

    • Niektóre funkcje, takie jak Content negotiation nie są dostępne.
    • Content-Type Produkcja jest podejmowana HttpResults przez wdrożenie.

Może HttpResults to być przydatne podczas udostępniania kodu między minimalnymi interfejsami API i internetowym interfejsem API.

Typ IResult

Microsoft.AspNetCore.Http.HttpResults Przestrzeń nazw zawiera klasy implementujące IResult interfejs. Interfejs IResult definiuje kontrakt reprezentujący wynik punktu końcowego HTTP. Statyczna klasa Results służy do tworzenia różnych IResult obiektów reprezentujących różne typy odpowiedzi.

W tabeli wyników wbudowanych przedstawiono typowe pomocniki wyników.

Spójrzmy na poniższy kod:

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

W poprzedniej akcji:

  • Kod stanu 404 jest zwracany, gdy produkt nie istnieje w bazie danych.
  • Kod stanu 200 jest zwracany z odpowiednim Product obiektem, gdy produkt istnieje, wygenerowany przez wynik.OK<T>().

Spójrzmy na poniższy kod:

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

W poprzedniej akcji:

  • Kod stanu 400 jest zwracany, gdy:
    • Atrybut został zastosowany, a walidacja [ApiController] modelu kończy się niepowodzeniem.
    • Opis produktu zawiera element "Widżet XYZ".
  • Kod stanu 201 jest generowany przez Results.Create metodę podczas tworzenia produktu. W tej ścieżce Product kodu obiekt jest udostępniany w treści odpowiedzi. Zostanie Location udostępniony nagłówek odpowiedzi zawierający adres URL nowo utworzonego produktu.

Wyniki<TResult1, typ TResultN>

Statyczna klasa TypedResults zwraca konkretną IResult implementację, która umożliwia użycie IResult jako typu zwracanego. Użycie konkretnego IResult wdrożenia zapewnia następującą korzyść w przypadku typu IResult:

  • [ProducesResponseType] Wszystkie atrybuty można wykluczyć, ponieważ implementacja HttpResult automatycznie przyczynia się do metadanych punktu końcowego.

Gdy jest potrzebnych wiele IResult typów zwracanych, zwracanie Results<TResult1, TResultN> jest preferowane w przypadku zwracania IResultwartości . Zwracanie jest preferowane Results<TResult1, TResultN> , ponieważ typy związków ogólnych automatycznie zachowują metadane punktu końcowego.

Typy Results<TResult1, TResultN> unii implementują niejawne operatory rzutowania, aby kompilator mógł automatycznie konwertować typy określone w argumentach ogólnych na wystąpienie typu unii. Ma to dodatkową zaletę zapewnienia sprawdzania czasu kompilacji, że program obsługi tras rzeczywiście zwraca tylko wyniki, które deklaruje. Próba zwrócenia typu, który nie jest zadeklarowany jako jeden z argumentów ogólnych, aby spowodować Results<> błąd kompilacji.

Spójrzmy na poniższy kod:

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

W poprzedniej akcji:

  • Kod stanu 404 jest zwracany, gdy produkt nie istnieje w bazie danych.
  • Kod stanu 200 jest zwracany z odpowiednim Product obiektem, gdy produkt istnieje, wygenerowany przez typedResults.OK<T>.
[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);
}

W poprzedniej akcji:

  • Kod stanu 400 jest zwracany, gdy:
    • Atrybut został zastosowany, a walidacja [ApiController] modelu kończy się niepowodzeniem.
    • Opis produktu zawiera element "Widżet XYZ".
  • Kod stanu 201 jest generowany przez TypedResults.Created metodę podczas tworzenia produktu. W tej ścieżce Product kodu obiekt jest udostępniany w treści odpowiedzi. Zostanie Location udostępniony nagłówek odpowiedzi zawierający adres URL nowo utworzonego produktu.

Dodatkowe zasoby

Wyświetl lub pobierz przykładowy kod (jak pobrać)

ASP.NET Core udostępnia następujące opcje dla typów zwracanych akcji kontrolera internetowego interfejsu API:

W tym artykule wyjaśniono, kiedy najbardziej odpowiednie jest użycie każdego typu zwracanego.

Określony typ

Najbardziej podstawowa akcja zwraca typ danych pierwotnych lub złożonych, na przykład string lub obiekt niestandardowy. Rozważmy następującą akcję, która zwraca kolekcję obiektów niestandardowych Product :

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

Bez znanych warunków ochrony przed zwróceniem określonego typu może wystarczyć. Poprzednia akcja nie akceptuje żadnych parametrów, więc weryfikacja ograniczeń parametrów nie jest wymagana.

Jeśli jest możliwe wiele typów zwracanych, często należy mieszać typ zwracany z typem ActionResult zwracanym typu pierwotnego lub złożonego. Aby uwzględnić ten typ akcji, konieczne jest rozwiązanie IActionResult lub ActionResult<T> . W tym artykule przedstawiono kilka przykładów wielu typów zwracanych.

Zwracanie wartości IEnumerable<T> lub IAsyncEnumerable<T>

Zobacz Return or for performance considerations (Zwracanie IEnumerable<T> lub IAsyncEnumerable<T> zagadnienia dotyczące wydajności).

ASP.NET Core buforuje wynik akcji, które zwracają IEnumerable<T> się przed zapisaniem ich w odpowiedzi. Rozważ zadeklarowanie typu zwrotnego podpisu akcji w IAsyncEnumerable<T> celu zagwarantowania asynchronicznej iteracji. Ostatecznie tryb iteracji jest oparty na zwracanym podstawowym typie betonowym, a wybrany formater wpływa na sposób przetwarzania wyniku:

  • W przypadku korzystania z System.Text.Json programu formatującego mvC opiera się na obsłudze System.Text.Json dodanej w celu przesyłania strumieniowego wyniku.
  • W przypadku używania Newtonsoft.Json lub z formaterami XML-based wynik jest buforowany.

Rozważmy następującą akcję, która zwraca rekordy produktów w cenie sprzedaży jako IEnumerable<Product>:

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

Odpowiednikiem IAsyncEnumerable<Product> poprzedniej akcji jest:

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

Typ IActionResult

Zwracany IActionResult typ jest odpowiedni, gdy w akcji można wykonać wiele ActionResult zwracanych typów. Typy ActionResult reprezentują różne kody stanu HTTP. Każda nie abstrakcyjna klasa wyprowadzająca z ActionResult kwalifikuje się jako prawidłowy typ zwracany. Niektóre typowe typy zwracane w tej kategorii to BadRequestResult (400), NotFoundResult (404) i OkObjectResult (200). Alternatywnie metody wygodne w ControllerBase klasie mogą służyć do zwracania ActionResult typów z akcji. Na przykład return BadRequest(); jest skróconą formą return new BadRequestResult();.

Ponieważ istnieje wiele typów zwracanych i ścieżek w tym typie akcji, konieczne jest liberalne użycie atrybutu [ProducesResponseType] . Ten atrybut tworzy bardziej opisowe szczegóły odpowiedzi dla stron pomocy interfejsu API sieci Web generowanych przez narzędzia, takie jak Swagger. [ProducesResponseType] wskazuje znane typy i kody stanu HTTP, które mają być zwracane przez akcję.

Akcja synchroniczna

Rozważmy następującą akcję synchroniczną, w której istnieją dwa możliwe typy zwracane:

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

W poprzedniej akcji:

  • Kod stanu 404 jest zwracany, gdy produkt reprezentowany przez id nie istnieje w bazowym magazynie danych. Metoda wygody NotFound jest wywoływana jako skrócona dla .return new NotFoundResult();
  • Kod stanu 200 jest zwracany z obiektem Product , gdy produkt istnieje. Metoda wygody Ok jest wywoływana jako skrócona dla .return new OkObjectResult(product);

Akcja asynchroniczna

Rozważ następującą akcję asynchroniczną, w której istnieją dwa możliwe typy zwracane:

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

W poprzedniej akcji:

  • Kod stanu 400 jest zwracany, gdy opis produktu zawiera element "Widżet XYZ". Metoda wygody BadRequest jest wywoływana jako skrócona dla .return new BadRequestResult();

  • Kod stanu 201 jest generowany przez CreatedAtAction metodę wygody podczas tworzenia produktu. Poniższy kod jest alternatywą dla wywołania metody CreatedAtAction:

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

    W poprzedniej ścieżce Product kodu obiekt jest udostępniany w treści odpowiedzi. Zostanie Location udostępniony nagłówek odpowiedzi zawierający adres URL nowo utworzonego produktu.

Na przykład poniższy model wskazuje, że żądania muszą zawierać Name właściwości i Description . Niepowodzenie podania Name i Description w żądaniu powoduje niepowodzenie walidacji modelu.

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

[ApiController] Jeśli atrybut zostanie zastosowany, błędy walidacji modelu spowodują wyświetlenie kodu stanu 400. Aby uzyskać więcej informacji, zobacz Automatyczne odpowiedzi HTTP 400.

ActionResult vs IActionResult

Poniższa sekcja jest porównywana ActionResult z IActionResult

ActionResult T<>, typ

ASP.NET Core zawiera typ zwracany ActionResult<T> dla akcji internetowego kontrolera interfejsu API. Umożliwia zwracanie typu pochodzącego z ActionResult lub zwracanie określonego typu. ActionResult<T> oferuje następujące korzyści wynikające z typu IActionResult:

  • [ProducesResponseType] Właściwość atrybutu Type można wykluczyć. Na przykład [ProducesResponseType(200, Type = typeof(Product))] jest uproszczony do [ProducesResponseType(200)]. Oczekiwany typ zwracany akcji jest wywnioskowany z elementu T w ActionResult<T>pliku .
  • Niejawne operatory rzutowania obsługują konwersję elementów i TActionResult na ActionResult<T>. T konwertuje na ObjectResultwartość , co oznacza return new ObjectResult(T); , że jest uproszczone na return T;.

Język C# nie obsługuje niejawnych operatorów rzutowania w interfejsach. W związku z tym konwersja interfejsu na konkretny typ jest niezbędna do użycia elementu ActionResult<T>. Na przykład użycie elementu IEnumerable w poniższym przykładzie nie działa:

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

Jedną z opcji naprawienia poprzedniego kodu jest zwrócenie _repository.GetProducts().ToList();polecenia .

Większość akcji ma określony typ zwracany. Podczas wykonywania akcji mogą wystąpić nieoczekiwane warunki, w tym przypadku określony typ nie jest zwracany. Na przykład parametr wejściowy akcji może zakończyć się niepowodzeniem weryfikacji modelu. W takim przypadku często zwracany jest odpowiedni ActionResult typ zamiast określonego typu.

Akcja synchroniczna

Rozważ akcję synchroniczną, w której istnieją dwa możliwe typy zwracane:

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

W poprzedniej akcji:

  • Kod stanu 404 jest zwracany, gdy produkt nie istnieje w bazie danych.
  • Kod stanu 200 jest zwracany z odpowiednim Product obiektem, gdy produkt istnieje.

Akcja asynchroniczna

Rozważ akcję asynchroniczną, w której istnieją dwa możliwe typy zwracane:

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

W poprzedniej akcji:

  • Kod stanu 400 (BadRequest) jest zwracany przez środowisko uruchomieniowe ASP.NET Core, gdy:
    • Atrybut został zastosowany, a walidacja [ApiController] modelu kończy się niepowodzeniem.
    • Opis produktu zawiera element "Widżet XYZ".
  • Kod stanu 201 jest generowany przez CreatedAtAction metodę podczas tworzenia produktu. W tej ścieżce Product kodu obiekt jest udostępniany w treści odpowiedzi. Zostanie Location udostępniony nagłówek odpowiedzi zawierający adres URL nowo utworzonego produktu.

Typ HttpResults

Oprócz wbudowanych typów wyników mvC (IActionResult i ActionResult<T>) ASP.NET Core obejmuje typy HttpResults , które mogą być używane zarówno w minimalnych interfejsach API, jak i w internetowym interfejsie API.

Inne niż typy wyników specyficzne dla wzorca MVC:HttpResults

  • To implementacja wyników przetwarzana przez wywołanie metody IResult.ExecuteAsync.

  • Nie korzysta ze skonfigurowanych formatów. Nieuwzmaganie skonfigurowanych formaterów oznacza:

    • Niektóre funkcje, takie jak Content negotiation nie są dostępne.
    • Content-Type Produkcja jest podejmowana HttpResults przez wdrożenie.

Może HttpResults to być przydatne podczas udostępniania kodu między minimalnymi interfejsami API i internetowym interfejsem API.

Typ IResult

Microsoft.AspNetCore.Http.HttpResults Przestrzeń nazw zawiera klasy implementujące IResult interfejs. Interfejs IResult definiuje kontrakt reprezentujący wynik punktu końcowego HTTP. Statyczna klasa Results służy do tworzenia różnych IResult obiektów reprezentujących różne typy odpowiedzi.

W tabeli wyników wbudowanych przedstawiono typowe pomocniki wyników.

Spójrzmy na poniższy kod:

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

W poprzedniej akcji:

  • Kod stanu 404 jest zwracany, gdy produkt nie istnieje w bazie danych.
  • Kod stanu 200 jest zwracany z odpowiednim Product obiektem, gdy produkt istnieje, wygenerowany przez wynik.OK<T>().

Spójrzmy na poniższy kod:

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

W poprzedniej akcji:

  • Kod stanu 400 jest zwracany, gdy:
    • Atrybut został zastosowany, a walidacja [ApiController] modelu kończy się niepowodzeniem.
    • Opis produktu zawiera element "Widżet XYZ".
  • Kod stanu 201 jest generowany przez Results.Create metodę podczas tworzenia produktu. W tej ścieżce Product kodu obiekt jest udostępniany w treści odpowiedzi. Zostanie Location udostępniony nagłówek odpowiedzi zawierający adres URL nowo utworzonego produktu.

Wyniki<TResult1, typ TResultN>

Statyczna klasa TypedResults zwraca konkretną IResult implementację, która umożliwia użycie IResult jako typu zwracanego. Użycie konkretnego IResult wdrożenia zapewnia następującą korzyść w przypadku typu IResult:

  • [ProducesResponseType] Wszystkie atrybuty można wykluczyć, ponieważ implementacja HttpResult automatycznie przyczynia się do metadanych punktu końcowego.

Gdy jest potrzebnych wiele IResult typów zwracanych, zwracanie Results<TResult1, TResultN> jest preferowane w przypadku zwracania IResultwartości . Zwracanie jest preferowane Results<TResult1, TResultN> , ponieważ typy związków ogólnych automatycznie zachowują metadane punktu końcowego.

Typy Results<TResult1, TResultN> unii implementują niejawne operatory rzutowania, aby kompilator mógł automatycznie konwertować typy określone w argumentach ogólnych na wystąpienie typu unii. Ma to dodatkową zaletę zapewnienia sprawdzania czasu kompilacji, że program obsługi tras rzeczywiście zwraca tylko wyniki, które deklaruje. Próba zwrócenia typu, który nie jest zadeklarowany jako jeden z argumentów ogólnych, aby spowodować Results<> błąd kompilacji.

Spójrzmy na poniższy kod:

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

W poprzedniej akcji:

  • Kod stanu 404 jest zwracany, gdy produkt nie istnieje w bazie danych.
  • Kod stanu 200 jest zwracany z odpowiednim Product obiektem, gdy produkt istnieje, wygenerowany przez typedResults.OK<T>.
[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);
}

W poprzedniej akcji:

  • Kod stanu 400 jest zwracany, gdy:
    • Atrybut został zastosowany, a walidacja [ApiController] modelu kończy się niepowodzeniem.
    • Opis produktu zawiera element "Widżet XYZ".
  • Kod stanu 201 jest generowany przez TypedResults.Create metodę podczas tworzenia produktu. W tej ścieżce Product kodu obiekt jest udostępniany w treści odpowiedzi. Zostanie Location udostępniony nagłówek odpowiedzi zawierający adres URL nowo utworzonego produktu.

Dodatkowe zasoby

Wyświetl lub pobierz przykładowy kod (jak pobrać)

ASP.NET Core oferuje następujące opcje dla typów zwracanych akcji kontrolera internetowego interfejsu API:

W tym dokumencie wyjaśniono, kiedy najbardziej odpowiednie jest użycie każdego typu zwracanego.

Określony typ

Najprostsza akcja zwraca typ danych pierwotnych lub złożonych (na przykład string lub typ obiektu niestandardowego). Rozważmy następującą akcję, która zwraca kolekcję obiektów niestandardowych Product :

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

Bez znanych warunków ochrony przed wykonaniem akcji, zwrócenie określonego typu może wystarczyć. Poprzednia akcja nie akceptuje żadnych parametrów, więc weryfikacja ograniczeń parametrów nie jest wymagana.

Jeśli jest możliwe wiele typów zwracanych, często należy mieszać typ zwracany z typem ActionResult zwracanym typu pierwotnego lub złożonego. Aby uwzględnić ten typ akcji, konieczne jest rozwiązanie IActionResult lub ActionResult<T> . W tym dokumencie przedstawiono kilka przykładów wielu typów zwracanych.

Zwracanie wartości IEnumerable<T> lub IAsyncEnumerable<T>

ASP.NET Core buforuje wynik akcji, które zwracają IEnumerable<T> się przed zapisaniem ich w odpowiedzi. Rozważ zadeklarowanie typu zwrotnego podpisu akcji w IAsyncEnumerable<T> celu zagwarantowania asynchronicznej iteracji. Ostatecznie tryb iteracji jest oparty na zwracanym podstawowym typie betonowym. MvC automatycznie buforuje każdy konkretny typ, który implementuje IAsyncEnumerable<T>.

Rozważmy następującą akcję, która zwraca rekordy produktów w cenie sprzedaży jako IEnumerable<Product>:

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

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

Odpowiednikiem IAsyncEnumerable<Product> poprzedniej akcji jest:

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

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

Typ IActionResult

Zwracany IActionResult typ jest odpowiedni, gdy w akcji można wykonać wiele ActionResult zwracanych typów. Typy ActionResult reprezentują różne kody stanu HTTP. Każda nie abstrakcyjna klasa wyprowadzająca z ActionResult kwalifikuje się jako prawidłowy typ zwracany. Niektóre typowe typy zwracane w tej kategorii to BadRequestResult (400), NotFoundResult (404) i OkObjectResult (200). Alternatywnie metody wygodne w ControllerBase klasie mogą służyć do zwracania ActionResult typów z akcji. Na przykład return BadRequest(); jest skróconą formą return new BadRequestResult();.

Ponieważ istnieje wiele typów zwracanych i ścieżek w tym typie akcji, konieczne jest liberalne użycie atrybutu [ProducesResponseType] . Ten atrybut tworzy bardziej opisowe szczegóły odpowiedzi dla stron pomocy interfejsu API sieci Web generowanych przez narzędzia, takie jak Swagger. [ProducesResponseType] wskazuje znane typy i kody stanu HTTP, które mają być zwracane przez akcję.

Akcja synchroniczna

Rozważmy następującą akcję synchroniczną, w której istnieją dwa możliwe typy zwracane:

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

W poprzedniej akcji:

  • Kod stanu 404 jest zwracany, gdy produkt reprezentowany przez id nie istnieje w bazowym magazynie danych. Metoda wygody NotFound jest wywoływana jako skrócona dla .return new NotFoundResult();
  • Kod stanu 200 jest zwracany z obiektem Product , gdy produkt istnieje. Metoda wygody Ok jest wywoływana jako skrócona dla .return new OkObjectResult(product);

Akcja asynchroniczna

Rozważ następującą akcję asynchroniczną, w której istnieją dwa możliwe typy zwracane:

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

W poprzedniej akcji:

  • Kod stanu 400 jest zwracany, gdy opis produktu zawiera element "Widżet XYZ". Metoda wygody BadRequest jest wywoływana jako skrócona dla .return new BadRequestResult();
  • Kod stanu 201 jest generowany przez CreatedAtAction metodę wygody podczas tworzenia produktu. Alternatywą dla wywołania CreatedAtAction jest return new CreatedAtActionResult(nameof(GetById), "Products", new { id = product.Id }, product);. W tej ścieżce Product kodu obiekt jest udostępniany w treści odpowiedzi. Zostanie Location udostępniony nagłówek odpowiedzi zawierający adres URL nowo utworzonego produktu.

Na przykład poniższy model wskazuje, że żądania muszą zawierać Name właściwości i Description . Niepowodzenie podania Name i Description w żądaniu powoduje niepowodzenie walidacji modelu.

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

[ApiController] Jeśli atrybut zostanie zastosowany, błędy walidacji modelu spowodują wyświetlenie kodu stanu 400. Aby uzyskać więcej informacji, zobacz Automatyczne odpowiedzi HTTP 400.

ActionResult vs IActionResult

Poniższa sekcja jest porównywana ActionResult z IActionResult

ActionResult T<>, typ

ASP.NET Core zawiera typ zwracany ActionResult<T> dla akcji internetowego kontrolera interfejsu API. Umożliwia zwracanie typu pochodzącego z ActionResult lub zwracania określonego typu. ActionResult<T> oferuje następujące korzyści wynikające z typu IActionResult:

  • [ProducesResponseType] Właściwość atrybutu Type można wykluczyć. Na przykład [ProducesResponseType(200, Type = typeof(Product))] jest uproszczony do [ProducesResponseType(200)]. Oczekiwany typ zwracany akcji jest w zamian wywnioskowany z elementu T w ActionResult<T>pliku .
  • Niejawne operatory rzutowania obsługują konwersję elementów i TActionResult na ActionResult<T>. T konwertuje na ObjectResultwartość , co oznacza return new ObjectResult(T); , że jest uproszczone na return T;.

Język C# nie obsługuje niejawnych operatorów rzutowania w interfejsach. W związku z tym konwersja interfejsu na konkretny typ jest niezbędna do użycia elementu ActionResult<T>. Na przykład użycie elementu IEnumerable w poniższym przykładzie nie działa:

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

Jedną z opcji naprawienia poprzedniego kodu jest zwrócenie _repository.GetProducts().ToList();polecenia .

Większość akcji ma określony typ zwracany. Podczas wykonywania akcji mogą wystąpić nieoczekiwane warunki, w tym przypadku określony typ nie jest zwracany. Na przykład parametr wejściowy akcji może zakończyć się niepowodzeniem weryfikacji modelu. W takim przypadku często zwracany jest odpowiedni ActionResult typ zamiast określonego typu.

Akcja synchroniczna

Rozważ akcję synchroniczną, w której istnieją dwa możliwe typy zwracane:

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

W poprzedniej akcji:

  • Kod stanu 404 jest zwracany, gdy produkt nie istnieje w bazie danych.
  • Kod stanu 200 jest zwracany z odpowiednim Product obiektem, gdy produkt istnieje.

Akcja asynchroniczna

Rozważ akcję asynchroniczną, w której istnieją dwa możliwe typy zwracane:

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

W poprzedniej akcji:

  • Kod stanu 400 (BadRequest) jest zwracany przez środowisko uruchomieniowe ASP.NET Core, gdy:
    • Atrybut został zastosowany, a walidacja [ApiController] modelu kończy się niepowodzeniem.
    • Opis produktu zawiera element "Widżet XYZ".
  • Kod stanu 201 jest generowany przez CreatedAtAction metodę podczas tworzenia produktu. W tej ścieżce Product kodu obiekt jest udostępniany w treści odpowiedzi. Zostanie Location udostępniony nagłówek odpowiedzi zawierający adres URL nowo utworzonego produktu.

Dodatkowe zasoby