Tipi restituiti dall'azione del controller nell'API Web ASP.NET Core

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 8 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 8 di questo articolo.

Visualizzare o scaricare il codice di esempio (procedura per il download)

ASP.NET Core offre le opzioni seguenti per i tipi restituiti di azioni del controller API Web:

Questo articolo illustra quando è più appropriato usare ogni tipo restituito.

Tipo specifico

L'azione più semplice restituisce un tipo di dati primitivo o complesso, ad esempio o string un oggetto personalizzato. Si consideri l'azione seguente, che restituisce una raccolta di oggetti Product personalizzati:

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

Senza condizioni note da proteggere, la restituzione di un tipo specifico potrebbe essere sufficiente. L'azione precedente non accetta parametri, quindi non è necessario eseguire la convalida dei vincoli dei parametri.

Quando sono possibili più tipi restituiti, è comune combinare un ActionResult tipo restituito con il tipo restituito primitivo o complesso. IActionResult o ActionResult<T> sono necessari per supportare questo tipo di azione. In questo articolo sono disponibili diversi esempi di più tipi restituiti.

Restituisce IEnumerable<T> o IAsyncEnumerable<T>

Vedere Restituzione IEnumerable<T> o IAsyncEnumerable<T> per considerazioni sulle prestazioni.

ASP.NET Core memorizza nel buffer il risultato delle azioni che restituiscono IEnumerable<T> prima di scriverle nella risposta. Valutare la possibilità di dichiarare il tipo restituito della firma dell'azione per IAsyncEnumerable<T> garantire l'iterazione asincrona. In definitiva, la modalità di iterazione si basa sul tipo concreto sottostante restituito e il formattatore selezionato influisce sulla modalità di elaborazione del risultato:

  • Quando si usa System.Text.Json il formattatore, MVC si basa sul supporto aggiunto System.Text.Json per trasmettere il risultato.
  • Quando si usa Newtonsoft.Json o con XML-based formattatori, il risultato viene memorizzato nel buffer.

Si consideri l'azione seguente, che restituisce i record di prodotto a prezzo di vendita come 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;
        }
    }
}

L'equivalente IAsyncEnumerable<Product> dell'azione precedente è:

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

Tipo IActionResult

Il IActionResult tipo restituito è appropriato quando in un'azione sono possibili più ActionResult tipi restituiti. I tipi ActionResult rappresentano vari codici di stato HTTP. Qualsiasi classe non astratta derivata da ActionResult qualifica come tipo restituito valido. Alcuni tipi restituiti comuni in questa categoria sono BadRequestResult (400), NotFoundResult (404) e OkObjectResult (200). In alternativa, i metodi pratici nella ControllerBase classe possono essere usati per restituire ActionResult tipi da un'azione. Ad esempio, return BadRequest(); è una forma abbreviata di return new BadRequestResult();.

Poiché in questo tipo di azione sono presenti più tipi restituiti e percorsi, è necessario l'uso liberale dell'attributo [ProducesResponseType] . Questo attributo produce dettagli di risposta più descrittivi per le pagine della Guida dell'API Web generate da strumenti come Swagger. [ProducesResponseType] indica i tipi noti e i codici di stato HTTP che l'azione deve restituire.

Azione sincrona

Si consideri la seguente azione sincrona che prevede due possibili tipi restituiti:

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

Nell'azione precedente:

  • Un codice di stato 404 viene restituito quando il prodotto rappresentato da id non esiste nell'archivio dati sottostante. Il NotFound metodo di praticità viene richiamato come abbreviato per return new NotFoundResult();.
  • Quando il prodotto esiste, viene restituito un codice di stato 200 con l'oggetto Product . Il Ok metodo di praticità viene richiamato come abbreviato per return new OkObjectResult(product);.

Azione asincrona

Si consideri la seguente azione asincrona che prevede due possibili tipi restituiti:

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

Nell'azione precedente:

  • Quando la descrizione del prodotto contiene "XYZ Widget", viene restituito un codice di stato 400. Il BadRequest metodo di praticità viene richiamato come abbreviato per return new BadRequestResult();.

  • Un codice di stato 201 viene generato dal CreatedAtAction metodo pratico quando viene creato un prodotto. Il codice seguente è un'alternativa alla chiamata CreatedAtActiona :

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

    Nel percorso di codice precedente, l'oggetto Product viene fornito nel corpo della risposta. Viene fornita un'intestazione Location di risposta contenente l'URL del prodotto appena creato.

Ad esempio, il modello seguente indica che le richieste devono includere le proprietà Name e Description. L'impossibilità di fornire Name e Description nella richiesta causa l'esito negativo della convalida del modello.

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

Se l'attributo viene applicato, gli errori di convalida del [ApiController] modello generano un codice di stato 400. Per altre informazioni, vedere Risposte HTTP 400 automatiche.

ActionResult e IActionResult

La sezione seguente viene confrontata ActionResult con IActionResult

Tipo ActionResult<T>

ASP.NET Core include il tipo restituito ActionResult<T> per le azioni del controller API Web. Consente di restituire un tipo derivato da ActionResult o restituire un tipo specifico. ActionResult<T> offre i vantaggi seguenti rispetto al tipo IActionResult:

  • La [ProducesResponseType] proprietà dell'attributo Type può essere esclusa. Ad esempio, [ProducesResponseType(200, Type = typeof(Product))] viene semplificato in [ProducesResponseType(200)]. Il tipo restituito previsto dell'azione viene dedotto da T in ActionResult<T>.
  • Operatori di cast impliciti supportano la conversione di T e ActionResult in ActionResult<T>. T converte in ObjectResult, il che significa return new ObjectResult(T); che è semplificato in return T;.

C# non supporta operatori di cast impliciti sulle interfacce. Di conseguenza, per la conversione dell'interfaccia in un tipo concreto è necessario usare ActionResult<T>. Ad esempio, usare IEnumerable nell'esempio seguente non funziona:

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

Una delle opzioni disponibili per correggere il codice precedente consiste nel restituire _repository.GetProducts().ToList();.

La maggior parte delle azioni presenta un tipo restituito specifico. Durante l'esecuzione di un'azione possono verificarsi condizioni impreviste, nel qual caso il tipo specifico non viene restituito. Ad esempio, il parametro di input di un'azione potrebbe non superare la convalida del modello. In tal caso, è consueto che venga restituito il tipo ActionResult appropriato anziché il tipo specifico.

Azione sincrona

Si consideri un'azione sincrona che prevede due possibili tipi restituiti:

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

Nell'azione precedente:

  • Quando il prodotto non esiste nel database, viene restituito un codice di stato 404.
  • Quando il prodotto esiste, viene restituito un codice di stato 200 con l'oggetto corrispondente Product .

Azione asincrona

Si consideri un'azione asincrona che prevede due possibili tipi restituiti:

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

Nell'azione precedente:

  • Un codice di stato 400 (BadRequest) viene restituito dal runtime di ASP.NET Core quando:
    • L'attributo [ApiController] è stato applicato e la convalida del modello ha esito negativo.
    • La descrizione del prodotto contiene "XYZ Widget".
  • Un codice di stato 201 viene generato dal CreatedAtAction metodo quando viene creato un prodotto. In questo percorso di codice l'oggetto Product viene fornito nel corpo della risposta. Viene fornita un'intestazione Location di risposta contenente l'URL del prodotto appena creato.

Tipo HttpResults

Oltre ai tipi di risultato predefiniti specifici di MVC (IActionResult e ActionResult T>), ASP.NET Core include i tipi HttpResults< che possono essere usati sia nelle API Minime che nell'API Web.

Diverso dai tipi di risultati specifici di MVC, ovvero HttpResults:

  • Implementazione dei risultati elaborata da una chiamata a IResult.ExecuteAsync.

  • Non sfrutta i formattatori configurati. Non sfruttando i formattatori configurati significa:

    • Alcune funzionalità come Content negotiation non sono disponibili.
    • L'oggetto prodotto Content-Type è deciso dall'implementazione HttpResults .

HttpResults Può essere utile quando si condivide il codice tra LE API minime e l'API Web.

Tipo IResult

Lo Microsoft.AspNetCore.Http.HttpResults spazio dei nomi contiene classi che implementano l'interfaccia IResult . L'interfaccia IResult definisce un contratto che rappresenta il risultato di un endpoint HTTP. La classe static Results viene usata per creare oggetti variabili IResult che rappresentano tipi diversi di risposte.

La tabella dei risultati predefiniti mostra gli helper dei risultati comuni.

Osservare il codice seguente:

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

Nell'azione precedente:

  • Quando il prodotto non esiste nel database, viene restituito un codice di stato 404.
  • Un codice di stato 200 viene restituito con l'oggetto corrispondente Product quando il prodotto esiste, generato da Results.Ok<T>().

Osservare il codice seguente:

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

Nell'azione precedente:

  • Quando viene restituito un codice di stato 400:
    • L'attributo [ApiController] è stato applicato e la convalida del modello ha esito negativo.
    • La descrizione del prodotto contiene "XYZ Widget".
  • Un codice di stato 201 viene generato dal Results.Create metodo quando viene creato un prodotto. In questo percorso di codice l'oggetto Product viene fornito nel corpo della risposta. Viene fornita un'intestazione Location di risposta contenente l'URL del prodotto appena creato.

Risultati<TResult1, tipo TResultN>

La classe Static TypedResults restituisce l'implementazione concreta IResult che consente di usare IResult come tipo restituito. L'utilizzo dell'implementazione concreta IResult offre il vantaggio seguente rispetto al tipo IResult:

  • Tutti gli [ProducesResponseType] attributi possono essere esclusi, poiché l'implementazione HttpResult contribuisce automaticamente ai metadati dell'endpoint.

Quando sono necessari più IResult tipi restituiti, la restituzione Results<TResult1, TResultN> è preferibile rispetto alla restituzione di IResult. La restituzione è preferibile Results<TResult1, TResultN> perché i tipi di unione generica mantengono automaticamente i metadati dell'endpoint.

I Results<TResult1, TResultN> tipi di unione implementano operatori cast impliciti in modo che il compilatore possa convertire automaticamente i tipi specificati negli argomenti generici in un'istanza del tipo di unione. Questo ha il vantaggio aggiunto di fornire un controllo in fase di compilazione che un gestore di route restituisce effettivamente solo i risultati che dichiara. Se si tenta di restituire un tipo non dichiarato come uno degli argomenti generici, si Results<> verifica un errore di compilazione.

Osservare il codice seguente:

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

Nell'azione precedente:

  • Quando il prodotto non esiste nel database, viene restituito un codice di stato 404.
  • Un codice di stato 200 viene restituito con l'oggetto corrispondente Product quando il prodotto esiste, generato da 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);
}

Nell'azione precedente:

  • Quando viene restituito un codice di stato 400:
    • L'attributo [ApiController] è stato applicato e la convalida del modello ha esito negativo.
    • La descrizione del prodotto contiene "XYZ Widget".
  • Un codice di stato 201 viene generato dal TypedResults.Created metodo quando viene creato un prodotto. In questo percorso di codice l'oggetto Product viene fornito nel corpo della risposta. Viene fornita un'intestazione Location di risposta contenente l'URL del prodotto appena creato.

Risorse aggiuntive

Visualizzare o scaricare il codice di esempio (procedura per il download)

ASP.NET Core offre le opzioni seguenti per i tipi restituiti di azioni del controller API Web:

Questo articolo illustra quando è più appropriato usare ogni tipo restituito.

Tipo specifico

L'azione più semplice restituisce un tipo di dati primitivo o complesso, ad esempio o string un oggetto personalizzato. Si consideri l'azione seguente, che restituisce una raccolta di oggetti Product personalizzati:

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

Senza condizioni note da proteggere, la restituzione di un tipo specifico potrebbe essere sufficiente. L'azione precedente non accetta parametri, quindi non è necessario eseguire la convalida dei vincoli dei parametri.

Quando sono possibili più tipi restituiti, è comune combinare un ActionResult tipo restituito con il tipo restituito primitivo o complesso. IActionResult o ActionResult<T> sono necessari per supportare questo tipo di azione. In questo articolo sono disponibili diversi esempi di più tipi restituiti.

Restituisce IEnumerable<T> o IAsyncEnumerable<T>

Vedere Restituzione IEnumerable<T> o IAsyncEnumerable<T> per considerazioni sulle prestazioni.

ASP.NET Core memorizza nel buffer il risultato delle azioni che restituiscono IEnumerable<T> prima di scriverle nella risposta. Valutare la possibilità di dichiarare il tipo restituito della firma dell'azione per IAsyncEnumerable<T> garantire l'iterazione asincrona. In definitiva, la modalità di iterazione si basa sul tipo concreto sottostante restituito e il formattatore selezionato influisce sulla modalità di elaborazione del risultato:

  • Quando si usa System.Text.Json il formattatore, MVC si basa sul supporto aggiunto System.Text.Json per trasmettere il risultato.
  • Quando si usa Newtonsoft.Json o con XML-based formattatori, il risultato viene memorizzato nel buffer.

Si consideri l'azione seguente, che restituisce i record di prodotto a prezzo di vendita come 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;
        }
    }
}

L'equivalente IAsyncEnumerable<Product> dell'azione precedente è:

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

Tipo IActionResult

Il IActionResult tipo restituito è appropriato quando in un'azione sono possibili più ActionResult tipi restituiti. I tipi ActionResult rappresentano vari codici di stato HTTP. Qualsiasi classe non astratta derivata da ActionResult qualifica come tipo restituito valido. Alcuni tipi restituiti comuni in questa categoria sono BadRequestResult (400), NotFoundResult (404) e OkObjectResult (200). In alternativa, i metodi pratici nella ControllerBase classe possono essere usati per restituire ActionResult tipi da un'azione. Ad esempio, return BadRequest(); è una forma abbreviata di return new BadRequestResult();.

Poiché in questo tipo di azione sono presenti più tipi restituiti e percorsi, è necessario l'uso liberale dell'attributo [ProducesResponseType] . Questo attributo produce dettagli di risposta più descrittivi per le pagine della Guida dell'API Web generate da strumenti come Swagger. [ProducesResponseType] indica i tipi noti e i codici di stato HTTP che l'azione deve restituire.

Azione sincrona

Si consideri la seguente azione sincrona che prevede due possibili tipi restituiti:

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

Nell'azione precedente:

  • Un codice di stato 404 viene restituito quando il prodotto rappresentato da id non esiste nell'archivio dati sottostante. Il NotFound metodo di praticità viene richiamato come abbreviato per return new NotFoundResult();.
  • Quando il prodotto esiste, viene restituito un codice di stato 200 con l'oggetto Product . Il Ok metodo di praticità viene richiamato come abbreviato per return new OkObjectResult(product);.

Azione asincrona

Si consideri la seguente azione asincrona che prevede due possibili tipi restituiti:

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

Nell'azione precedente:

  • Quando la descrizione del prodotto contiene "XYZ Widget", viene restituito un codice di stato 400. Il BadRequest metodo di praticità viene richiamato come abbreviato per return new BadRequestResult();.

  • Un codice di stato 201 viene generato dal CreatedAtAction metodo pratico quando viene creato un prodotto. Il codice seguente è un'alternativa alla chiamata CreatedAtActiona :

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

    Nel percorso di codice precedente, l'oggetto Product viene fornito nel corpo della risposta. Viene fornita un'intestazione Location di risposta contenente l'URL del prodotto appena creato.

Ad esempio, il modello seguente indica che le richieste devono includere le proprietà Name e Description. L'impossibilità di fornire Name e Description nella richiesta causa l'esito negativo della convalida del modello.

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

Se l'attributo viene applicato, gli errori di convalida del [ApiController] modello generano un codice di stato 400. Per altre informazioni, vedere Risposte HTTP 400 automatiche.

ActionResult e IActionResult

La sezione seguente viene confrontata ActionResult con IActionResult

Tipo ActionResult<T>

ASP.NET Core include il tipo restituito ActionResult<T> per le azioni del controller API Web. Consente di restituire un tipo derivato da ActionResult o restituire un tipo specifico. ActionResult<T> offre i vantaggi seguenti rispetto al tipo IActionResult:

  • La [ProducesResponseType] proprietà dell'attributo Type può essere esclusa. Ad esempio, [ProducesResponseType(200, Type = typeof(Product))] viene semplificato in [ProducesResponseType(200)]. Il tipo restituito previsto dell'azione viene dedotto da T in ActionResult<T>.
  • Operatori di cast impliciti supportano la conversione di T e ActionResult in ActionResult<T>. T converte in ObjectResult, il che significa return new ObjectResult(T); che è semplificato in return T;.

C# non supporta operatori di cast impliciti sulle interfacce. Di conseguenza, per la conversione dell'interfaccia in un tipo concreto è necessario usare ActionResult<T>. Ad esempio, usare IEnumerable nell'esempio seguente non funziona:

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

Una delle opzioni disponibili per correggere il codice precedente consiste nel restituire _repository.GetProducts().ToList();.

La maggior parte delle azioni presenta un tipo restituito specifico. Durante l'esecuzione di un'azione possono verificarsi condizioni impreviste, nel qual caso il tipo specifico non viene restituito. Ad esempio, il parametro di input di un'azione potrebbe non superare la convalida del modello. In tal caso, è consueto che venga restituito il tipo ActionResult appropriato anziché il tipo specifico.

Azione sincrona

Si consideri un'azione sincrona che prevede due possibili tipi restituiti:

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

Nell'azione precedente:

  • Quando il prodotto non esiste nel database, viene restituito un codice di stato 404.
  • Quando il prodotto esiste, viene restituito un codice di stato 200 con l'oggetto corrispondente Product .

Azione asincrona

Si consideri un'azione asincrona che prevede due possibili tipi restituiti:

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

Nell'azione precedente:

  • Un codice di stato 400 (BadRequest) viene restituito dal runtime di ASP.NET Core quando:
    • L'attributo [ApiController] è stato applicato e la convalida del modello ha esito negativo.
    • La descrizione del prodotto contiene "XYZ Widget".
  • Un codice di stato 201 viene generato dal CreatedAtAction metodo quando viene creato un prodotto. In questo percorso di codice l'oggetto Product viene fornito nel corpo della risposta. Viene fornita un'intestazione Location di risposta contenente l'URL del prodotto appena creato.

Tipo HttpResults

Oltre ai tipi di risultato predefiniti specifici di MVC (IActionResult e ActionResult T>), ASP.NET Core include i tipi HttpResults< che possono essere usati sia nelle API Minime che nell'API Web.

Diverso dai tipi di risultati specifici di MVC, ovvero HttpResults:

  • Implementazione dei risultati elaborata da una chiamata a IResult.ExecuteAsync.

  • Non sfrutta i formattatori configurati. Non sfruttando i formattatori configurati significa:

    • Alcune funzionalità come Content negotiation non sono disponibili.
    • L'oggetto prodotto Content-Type è deciso dall'implementazione HttpResults .

HttpResults Può essere utile quando si condivide il codice tra LE API minime e l'API Web.

Tipo IResult

Lo Microsoft.AspNetCore.Http.HttpResults spazio dei nomi contiene classi che implementano l'interfaccia IResult . L'interfaccia IResult definisce un contratto che rappresenta il risultato di un endpoint HTTP. La classe static Results viene usata per creare oggetti variabili IResult che rappresentano tipi diversi di risposte.

La tabella dei risultati predefiniti mostra gli helper dei risultati comuni.

Osservare il codice seguente:

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

Nell'azione precedente:

  • Quando il prodotto non esiste nel database, viene restituito un codice di stato 404.
  • Un codice di stato 200 viene restituito con l'oggetto corrispondente Product quando il prodotto esiste, generato da Results.Ok<T>().

Osservare il codice seguente:

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

Nell'azione precedente:

  • Quando viene restituito un codice di stato 400:
    • L'attributo [ApiController] è stato applicato e la convalida del modello ha esito negativo.
    • La descrizione del prodotto contiene "XYZ Widget".
  • Un codice di stato 201 viene generato dal Results.Create metodo quando viene creato un prodotto. In questo percorso di codice l'oggetto Product viene fornito nel corpo della risposta. Viene fornita un'intestazione Location di risposta contenente l'URL del prodotto appena creato.

Risultati<TResult1, tipo TResultN>

La classe Static TypedResults restituisce l'implementazione concreta IResult che consente di usare IResult come tipo restituito. L'utilizzo dell'implementazione concreta IResult offre il vantaggio seguente rispetto al tipo IResult:

  • Tutti gli [ProducesResponseType] attributi possono essere esclusi, poiché l'implementazione HttpResult contribuisce automaticamente ai metadati dell'endpoint.

Quando sono necessari più IResult tipi restituiti, la restituzione Results<TResult1, TResultN> è preferibile rispetto alla restituzione di IResult. La restituzione è preferibile Results<TResult1, TResultN> perché i tipi di unione generica mantengono automaticamente i metadati dell'endpoint.

I Results<TResult1, TResultN> tipi di unione implementano operatori cast impliciti in modo che il compilatore possa convertire automaticamente i tipi specificati negli argomenti generici in un'istanza del tipo di unione. Questo ha il vantaggio aggiunto di fornire un controllo in fase di compilazione che un gestore di route restituisce effettivamente solo i risultati che dichiara. Se si tenta di restituire un tipo non dichiarato come uno degli argomenti generici, si Results<> verifica un errore di compilazione.

Osservare il codice seguente:

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

Nell'azione precedente:

  • Quando il prodotto non esiste nel database, viene restituito un codice di stato 404.
  • Un codice di stato 200 viene restituito con l'oggetto corrispondente Product quando il prodotto esiste, generato da 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);
}

Nell'azione precedente:

  • Quando viene restituito un codice di stato 400:
    • L'attributo [ApiController] è stato applicato e la convalida del modello ha esito negativo.
    • La descrizione del prodotto contiene "XYZ Widget".
  • Un codice di stato 201 viene generato dal TypedResults.Create metodo quando viene creato un prodotto. In questo percorso di codice l'oggetto Product viene fornito nel corpo della risposta. Viene fornita un'intestazione Location di risposta contenente l'URL del prodotto appena creato.

Risorse aggiuntive

Visualizzare o scaricare il codice di esempio (procedura per il download)

ASP.NET Core offre le opzioni seguenti per i tipi restituiti di azioni del controller API Web:

Questo documento spiega quando è più appropriato utilizzare ogni tipo restituito.

Tipo specifico

L'azione più semplice restituisce un tipo di dati primitivo o complesso, ad esempio, string o un tipo di oggetto personalizzato. Si consideri l'azione seguente, che restituisce una raccolta di oggetti Product personalizzati:

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

In assenza di condizioni note da cui proteggersi durante l'esecuzione dell'azione, la restituzione di un tipo specifico potrebbe essere sufficiente. L'azione precedente non accetta parametri, quindi non è necessario eseguire la convalida dei vincoli dei parametri.

Quando sono possibili più tipi restituiti, è comune combinare un ActionResult tipo restituito con il tipo restituito primitivo o complesso. IActionResult o ActionResult<T> sono necessari per supportare questo tipo di azione. In questo documento vengono forniti diversi esempi di più tipi restituiti.

Restituisce IEnumerable<T> o IAsyncEnumerable<T>

ASP.NET Core memorizza nel buffer il risultato delle azioni che restituiscono IEnumerable<T> prima di scriverle nella risposta. Valutare la possibilità di dichiarare il tipo restituito della firma dell'azione per IAsyncEnumerable<T> garantire l'iterazione asincrona. In definitiva, la modalità di iterazione si basa sul tipo concreto sottostante restituito. MVC memorizza automaticamente nel buffer qualsiasi tipo concreto che implementa IAsyncEnumerable<T>.

Si consideri l'azione seguente, che restituisce i record di prodotto a prezzo di vendita come IEnumerable<Product>:

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

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

L'equivalente IAsyncEnumerable<Product> dell'azione precedente è:

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

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

Tipo IActionResult

Il IActionResult tipo restituito è appropriato quando in un'azione sono possibili più ActionResult tipi restituiti. I tipi ActionResult rappresentano vari codici di stato HTTP. Qualsiasi classe non astratta derivata da ActionResult qualifica come tipo restituito valido. Alcuni tipi restituiti comuni in questa categoria sono BadRequestResult (400), NotFoundResult (404) e OkObjectResult (200). In alternativa, i metodi pratici nella ControllerBase classe possono essere usati per restituire ActionResult tipi da un'azione. Ad esempio, return BadRequest(); è una forma abbreviata di return new BadRequestResult();.

Poiché in questo tipo di azione sono presenti più tipi restituiti e percorsi, è necessario l'uso liberale dell'attributo [ProducesResponseType] . Questo attributo produce dettagli di risposta più descrittivi per le pagine della Guida dell'API Web generate da strumenti come Swagger. [ProducesResponseType] indica i tipi noti e i codici di stato HTTP che l'azione deve restituire.

Azione sincrona

Si consideri la seguente azione sincrona che prevede due possibili tipi restituiti:

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

Nell'azione precedente:

  • Un codice di stato 404 viene restituito quando il prodotto rappresentato da id non esiste nell'archivio dati sottostante. Il NotFound metodo di praticità viene richiamato come abbreviato per return new NotFoundResult();.
  • Quando il prodotto esiste, viene restituito un codice di stato 200 con l'oggetto Product . Il Ok metodo di praticità viene richiamato come abbreviato per return new OkObjectResult(product);.

Azione asincrona

Si consideri la seguente azione asincrona che prevede due possibili tipi restituiti:

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

Nell'azione precedente:

  • Quando la descrizione del prodotto contiene "XYZ Widget", viene restituito un codice di stato 400. Il BadRequest metodo di praticità viene richiamato come abbreviato per return new BadRequestResult();.
  • Un codice di stato 201 viene generato dal CreatedAtAction metodo pratico quando viene creato un prodotto. Un'alternativa alla chiamata CreatedAtAction è return new CreatedAtActionResult(nameof(GetById), "Products", new { id = product.Id }, product);. In questo percorso di codice l'oggetto Product viene fornito nel corpo della risposta. Viene fornita un'intestazione Location di risposta contenente l'URL del prodotto appena creato.

Ad esempio, il modello seguente indica che le richieste devono includere le proprietà Name e Description. L'impossibilità di fornire Name e Description nella richiesta causa l'esito negativo della convalida del modello.

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

Se l'attributo viene applicato, gli errori di convalida del [ApiController] modello generano un codice di stato 400. Per altre informazioni, vedere Risposte HTTP 400 automatiche.

ActionResult e IActionResult

La sezione seguente viene confrontata ActionResult con IActionResult

Tipo ActionResult<T>

ASP.NET Core include il tipo restituito ActionResult<T> per le azioni del controller API Web. Consente di restituire un tipo derivato da ActionResult o restituire un tipo specifico. ActionResult<T> offre i vantaggi seguenti rispetto al tipo IActionResult:

  • La [ProducesResponseType] proprietà dell'attributo Type può essere esclusa. Ad esempio, [ProducesResponseType(200, Type = typeof(Product))] viene semplificato in [ProducesResponseType(200)]. Il tipo restituito previsto dell'azione viene invece dedotto da T in ActionResult<T>.
  • Operatori di cast impliciti supportano la conversione di T e ActionResult in ActionResult<T>. T converte in ObjectResult, il che significa return new ObjectResult(T); che è semplificato in return T;.

C# non supporta operatori di cast impliciti sulle interfacce. Di conseguenza, per la conversione dell'interfaccia in un tipo concreto è necessario usare ActionResult<T>. Ad esempio, usare IEnumerable nell'esempio seguente non funziona:

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

Una delle opzioni disponibili per correggere il codice precedente consiste nel restituire _repository.GetProducts().ToList();.

La maggior parte delle azioni presenta un tipo restituito specifico. Durante l'esecuzione di un'azione possono verificarsi condizioni impreviste, nel qual caso il tipo specifico non viene restituito. Ad esempio, il parametro di input di un'azione potrebbe non superare la convalida del modello. In tal caso, è consueto che venga restituito il tipo ActionResult appropriato anziché il tipo specifico.

Azione sincrona

Si consideri un'azione sincrona che prevede due possibili tipi restituiti:

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

Nell'azione precedente:

  • Quando il prodotto non esiste nel database, viene restituito un codice di stato 404.
  • Quando il prodotto esiste, viene restituito un codice di stato 200 con l'oggetto corrispondente Product .

Azione asincrona

Si consideri un'azione asincrona che prevede due possibili tipi restituiti:

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

Nell'azione precedente:

  • Un codice di stato 400 (BadRequest) viene restituito dal runtime di ASP.NET Core quando:
    • L'attributo [ApiController] è stato applicato e la convalida del modello ha esito negativo.
    • La descrizione del prodotto contiene "XYZ Widget".
  • Un codice di stato 201 viene generato dal CreatedAtAction metodo quando viene creato un prodotto. In questo percorso di codice l'oggetto Product viene fornito nel corpo della risposta. Viene fornita un'intestazione Location di risposta contenente l'URL del prodotto appena creato.

Risorse aggiuntive