Creare API Web con ASP.NET Core

ASP.NET Core supporta la creazione di API Web usando controller o API minime. I controller in un'API Web sono classi che derivano da ControllerBase. I controller vengono attivati ed eliminati per ogni richiesta.

Questo articolo illustra come usare i controller per gestire le richieste di API Web. Per informazioni sulla creazione di API Web senza controller, vedere Esercitazione: Creare un'API minima con ASP.NET Core.

Classe ControllerBase

Un'API web basata su controller è costituita da una o più classi di controller che derivano da ControllerBase. Il modello di progetto di API Web fornisce un controller iniziale:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

I controller API Web devono in genere derivare da ControllerBase piuttosto che da Controller. La classe Controller deriva da ControllerBase e aggiunge il supporto per le visualizzazioni, pertanto è progettata per la gestione delle pagine Web e non per le richieste di API Web. Se lo stesso controller deve supportare le visualizzazioni e le API Web, derivarlo da Controller.

La classe ControllerBase offre molti metodi e proprietà utili per la gestione delle richieste HTTP. Ad esempio, CreatedAtAction restituisce un codice di stato 201:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

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

La tabella seguente contiene esempi di metodi in ControllerBase.

Method Note
BadRequest Restituisce il codice di stato 400.
NotFound Restituisce il codice di stato 404.
PhysicalFile Restituisce un file.
TryUpdateModelAsync Richiama l'associazione di modelli.
TryValidateModel Richiama la convalida dei modelli.

Per un elenco di tutti i metodi e proprietà disponibili, vedere ControllerBase.

Attributi

Lo spazio dei nomi Microsoft.AspNetCore.Mvc include attributi che possono essere usati per configurare il comportamento del controller di API Web e i metodi di azione. L'esempio seguente usa attributi per specificare il verbo dell'azione HTTP supportato e tutti i codici di stato HTTP noti che potrebbero essere restituiti:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

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

Di seguito sono riportati altri esempi degli attributi disponibili.

Attributo Note
[Route] Specifica il modello di URL per un controller o un'azione.
[Bind] Specifica il prefisso e le proprietà da includere per l'associazione di modelli.
[HttpGet] Identifica un'azione che supporta il verbo dell'azione HTTP GET.
[Consumes] Specifica i tipi di dati accettati da un'azione.
[Produces] Specifica i tipi di dati restituiti da un'azione.

Per un elenco che include gli attributi disponibili, vedere lo spazio dei nomi Microsoft.AspNetCore.Mvc.

Attributo ApiController

L'attributo [ApiController] può essere applicato a una classe controller per abilitare i comportamenti opinionati specifici dell'API:

Attributo in controller specifici

L'attributo [ApiController] può essere applicato a controller specifici, come illustrato nell'esempio seguente dal modello di progetto:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Attributo in più controller

Un approccio per usare l'attributo su più di un controller consiste nel creare una classe di controller di base personalizzata annotata con l'attributo [ApiController]. L'esempio seguente illustra una classe di base personalizzata e un controller che deriva da tale classe:

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class PetsController : MyControllerBase

Attributo in un assembly

L'attributo [ApiController] può essere applicato a un assembly. Se l'attributo [ApiController] viene applicato a un assembly, tutti i controller al suo interno avranno l'attributo [ApiController] applicato. Non esiste alcun modo per rifiutare esplicitamente singoli controller. Applicare l'attributo a livello di assembly al file Program.cs:

using Microsoft.AspNetCore.Mvc;
[assembly: ApiController]

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Requisiti del routing degli attributi

Con l'attributo [ApiController] il routing degli attributi è un requisito. Ad esempio:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Le azioni non sono accessibili tramite le route convenzionali definite da UseEndpoints, UseMvc o UseMvcWithDefaultRoute.

Risposte HTTP 400 automatiche

Con l'attributo [ApiController] gli errori di convalida del modello attivano automaticamente una risposta HTTP 400. Di conseguenza è necessario il codice seguente in un metodo di azione:

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

ASP.NET Core MVC usa il filtro di azione ModelStateInvalidFilter per eseguire il controllo precedente.

Risposta BadRequest predefinita

Il tipo di risposta predefinito per una risposta HTTP 400 è ValidationProblemDetails. Il corpo della risposta seguente è un esempio del tipo serializzato:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

Il tipo ValidationProblemDetails:

  • Fornisce un formato leggibile dal computer per specificare gli errori nelle risposte dell'API Web.
  • È conforme alla specifica RFC 7807.

Per rendere coerenti le risposte automatiche e personalizzate, chiamare il metodo ValidationProblem anziché BadRequest. ValidationProblem restituisce un oggetto ValidationProblemDetails oltre alla risposta automatica.

Registrare le risposte 400 automatiche

Per registrare le risposte 400 automatiche, impostare la proprietà delegata InvalidModelStateResponseFactory per eseguire l'elaborazione personalizzata. Per impostazione predefinita, InvalidModelStateResponseFactory usa ProblemDetailsFactory per creare un'istanza di ValidationProblemDetails.

L'esempio seguente illustra come recuperare un'istanza di ILogger<TCategoryName> per registrare informazioni su una risposta 400 automatica:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
      // To preserve the default behavior, capture the original delegate to call later.
        var builtInFactory = options.InvalidModelStateResponseFactory;

        options.InvalidModelStateResponseFactory = context =>
        {
            var logger = context.HttpContext.RequestServices
                                .GetRequiredService<ILogger<Program>>();

            // Perform logging here.
            // ...

            // Invoke the default behavior, which produces a ValidationProblemDetails
            // response.
            // To produce a custom response, return a different implementation of 
            // IActionResult instead.
            return builtInFactory(context);
        };
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Disabilitare la risposta 400 automatica

Per disabilitare il comportamento automatico per gli errori 400, impostare la proprietà SuppressModelStateInvalidFilter su true. Aggiungere il codice evidenziato seguente:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Inferenza del parametro di origine di associazione

Un attributo di origine di associazione definisce la posizione in cui viene trovato il valore del parametro di un'azione. Esistono gli attributi di origine di associazione seguente:

Attributo Origine di associazione
[FromBody] Corpo della richiesta
[FromForm] Dati di modulo nel corpo della richiesta
[FromHeader] Intestazione della richiesta
[FromQuery] Parametri della stringa di query della richiesta
[FromRoute] Dati della route della richiesta corrente
[FromServices] Il servizio richiesta inserito come parametro di azione
[AsParameters] Parametri del metodo

Avviso

Non usare [FromRoute] quando i valori potrebbero contenere %2f (vale a dire /). %2f non sarà convertito in / rimuovendo i caratteri di escape. Usare [FromQuery] se il valore potrebbe contenere %2f.

Senza l'attributo [ApiController] o altri attributi di origine di associazione come [FromQuery], il runtime di ASP.NET Core tenta di usare lo strumento di associazione di modelli a oggetti complesso. Lo strumento di associazione di modelli a oggetti complesso estrae i dati dal provider di valori in un ordine definito.

Nell'esempio seguente, l'attributo [FromQuery] indica che il valore del parametro discontinuedOnly è specificato nella stringa di query dell'URL della richiesta:

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

L'attributo [ApiController] applica le regole di inferenza per le origini dati predefinite dei parametri di azione. Queste regole consentono di evitare di dover identificare le origini di associazione manualmente applicando attributi ai parametri di azione. Il comportamento delle regole di inferenza per le origini di associazione è il seguente:

  • [FromServices] viene dedotto per parametri di tipo complessi registrati nel contenitore di inserimento delle dipendenze.
  • [FromBody] viene dedotto per parametri di tipo complessi non registrati nel contenitore di inserimento delle dipendenze. Un'eccezione alla regola di inferenza [FromBody] è costituita dai tipi predefiniti complessi con un significato speciale, ad esempio IFormCollection e CancellationToken. Il codice di inferenza di origine di associazione ignora tali tipi speciali.
  • [FromForm] viene dedotto per i parametri di azione di tipo IFormFile e IFormFileCollection. Non viene dedotto per i tipi semplici o definiti dall'utente.
  • [FromRoute] viene dedotto per i nomi di parametro di azione corrispondenti a un parametro nel modello di route. Quando più di una route corrisponde a un parametro di azione, tutti i valori di route vengono considerati [FromRoute].
  • [FromQuery] viene dedotto per tutti gli altri parametri di azione.

Note sulla regola di inferenza FromBody

[FromBody] non viene dedotto per i tipi semplici, ad esempio string o int. Pertanto, l'attributo [FromBody] deve essere usato per i tipi semplici se è necessaria tale funzionalità.

Quando a un'azione sono associati più parametri dal corpo della richiesta, viene generata un'eccezione. Tutte le firme di metodo di azione seguenti, ad esempio, causano un'eccezione:

  • [FromBody] dedotto per entrambi, perché si tratta di tipi complessi.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • [FromBody] attributo per uno, dedotto per l'altro perché è un tipo complesso.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • [FromBody] attributo per entrambi.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Note sull'inferenza FromServices

L'associazione di parametri associa i parametri tramite l'inserimento delle dipendenze quando il tipo è configurato come servizio. Ciò significa che non è necessario applicare in modo esplicito l'attributo [FromServices] a un parametro. Nel codice seguente entrambe le azioni restituiscono l'ora:

[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
    public ActionResult GetWithAttribute([FromServices] IDateTime dateTime) 
                                                        => Ok(dateTime.Now);

    [Route("noAttribute")]
    public ActionResult Get(IDateTime dateTime) => Ok(dateTime.Now);
}

In rari casi, l'inserimento automatico delle dipendenze può interrompere le app con un tipo di inserimento delle dipendenze accettate anche nei metodi di azione di un controller API. Non è comune avere un tipo di inserimento delle dipendenze e come argomento in un'azione del controller API.

Per disabilitare l'inferenza [FromServices] per un singolo parametro di azione, applicare l'attributo di origine dell'associazione desiderato al parametro. Ad esempio, applicare l'attributo [FromBody] a un parametro di azione che deve essere associato dal corpo della richiesta.

Per disabilitare l'inferenza [FromServices] a livello globale, impostare DisableImplicitFromServicesParameters su true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddSingleton<IDateTime, SystemDateTime>();

builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.DisableImplicitFromServicesParameters = true;
});

var app = builder.Build();

app.MapControllers();

app.Run();

I tipi vengono controllati all'avvio dell'app con IServiceProviderIsService per determinare se un argomento in un'azione del controller API proviene dall'inserimento delle dipendenze o dalle altre origini.

Il meccanismo per dedurre l'origine dell'associazione dei parametri di azione del controller API usa le regole seguenti:

Disabilitare le regole di inferenza

Per disabilitare l'inferenza delle origini di associazione, impostare SuppressInferBindingSourcesForParameters su true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Inferenza di richieste multipart/form-data

L'attributo [ApiController] applica una regola di inferenza per i parametri di azione di tipo IFormFile e IFormFileCollection. Il tipo di contenuto della richiesta multipart/form-data viene dedotto per questi tipi.

Per disabilitare il comportamento predefinito, impostare la proprietà SuppressConsumesConstraintForFormFileParameters su true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Dettagli del problema per i codici di stato di errore

MVC trasforma un risultato di errore (un risultato con codice di stato 400 o superiore) in un risultato con ProblemDetails. Il tipo ProblemDetails si basa sulla specifica RFC 7807 per fornire dettagli sull'errore leggibili dal computer in una risposta HTTP.

Si consideri il codice seguente in un'azione del controller:

if (pet == null)
{
    return NotFound();
}

Il metodo NotFound produce un codice di stato HTTP 404 con un corpo ProblemDetails. Ad esempio:

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Disabilitare la risposta ProblemDetails

La creazione automatica di ProblemDetails per i codici di stato di errore è disabilitata quando la proprietà SuppressMapClientErrors è impostata su true. Aggiungere il codice seguente:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Definire i tipi di contenuto della richiesta supportati con l'attributo [Consumes]

Per impostazione predefinita, un'azione supporta tutti i tipi di contenuto della richiesta disponibili. Ad esempio, se un'app è configurata per supportare i formattatori di inputJSON e XML, un'azione supporta più tipi di contenuto, inclusi application/json e application/xml.

L'attributo [Consumes] consente a un'azione di limitare i tipi di contenuto della richiesta supportati. Applicare l'attributo [Consumes] a un'azione o a un controller, specificando uno o più tipi di contenuto:

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

Nel codice precedente l'azione CreateProduct specifica il tipo di contenuto application/xml. Le richieste instradate a questa azione devono specificare un'intestazione Content-Typeapplication/xml. Le richieste che non specificano un'intestazione Content-Typeapplication/xml generano una risposta 415 Tipo di supporto non supportato.

L'attributo [Consumes] consente a un'azione di influenzare la relativa selezione in base al tipo di contenuto di una richiesta in ingresso applicando un vincolo di tipo. Si consideri l'esempio seguente:

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

Nel codice precedente ConsumesController è configurato per gestire le richieste inviate all'URL https://localhost:5001/api/Consumes. Le azioni del controller PostJson e PostForm gestiscono le richieste POST con lo stesso URL. Senza l'attributo [Consumes] che applica un vincolo di tipo, viene generata un'eccezione di corrispondenza ambigua.

L'attributo [Consumes] viene applicato a entrambe le azioni. L'azione PostJson gestisce le richieste inviate con un'intestazione Content-Typeapplication/json. L'azione PostForm gestisce le richieste inviate con un'intestazione Content-Typeapplication/x-www-form-urlencoded.

Risorse aggiuntive

ASP.NET Core supporta la creazione di API Web usando controller o API minime. I controller in un'API Web sono classi che derivano da ControllerBase. Questo articolo illustra come usare i controller per gestire le richieste di API Web. Per informazioni sulla creazione di API Web senza controller, vedere Esercitazione: Creare un'API minima con ASP.NET Core.

Classe ControllerBase

Un'API web basata su controller è costituita da una o più classi di controller che derivano da ControllerBase. Il modello di progetto di API Web fornisce un controller iniziale:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

I controller API Web devono in genere derivare da ControllerBase piuttosto che da Controller. La classe Controller deriva da ControllerBase e aggiunge il supporto per le visualizzazioni, pertanto è progettata per la gestione delle pagine Web e non per le richieste di API Web. Se lo stesso controller deve supportare le visualizzazioni e le API Web, derivarlo da Controller.

La classe ControllerBase offre molti metodi e proprietà utili per la gestione delle richieste HTTP. Ad esempio, CreatedAtAction restituisce un codice di stato 201:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

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

La tabella seguente contiene esempi di metodi in ControllerBase.

Method Note
BadRequest Restituisce il codice di stato 400.
NotFound Restituisce il codice di stato 404.
PhysicalFile Restituisce un file.
TryUpdateModelAsync Richiama l'associazione di modelli.
TryValidateModel Richiama la convalida dei modelli.

Per un elenco di tutti i metodi e proprietà disponibili, vedere ControllerBase.

Attributi

Lo spazio dei nomi Microsoft.AspNetCore.Mvc include attributi che possono essere usati per configurare il comportamento del controller di API Web e i metodi di azione. L'esempio seguente usa attributi per specificare il verbo dell'azione HTTP supportato e tutti i codici di stato HTTP noti che potrebbero essere restituiti:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

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

Di seguito sono riportati altri esempi degli attributi disponibili.

Attributo Note
[Route] Specifica il modello di URL per un controller o un'azione.
[Bind] Specifica il prefisso e le proprietà da includere per l'associazione di modelli.
[HttpGet] Identifica un'azione che supporta il verbo dell'azione HTTP GET.
[Consumes] Specifica i tipi di dati accettati da un'azione.
[Produces] Specifica i tipi di dati restituiti da un'azione.

Per un elenco che include gli attributi disponibili, vedere lo spazio dei nomi Microsoft.AspNetCore.Mvc.

Attributo ApiController

L'attributo [ApiController] può essere applicato a una classe controller per abilitare i comportamenti opinionati specifici dell'API:

Attributo in controller specifici

L'attributo [ApiController] può essere applicato a controller specifici, come illustrato nell'esempio seguente dal modello di progetto:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Attributo in più controller

Un approccio per usare l'attributo su più di un controller consiste nel creare una classe di controller di base personalizzata annotata con l'attributo [ApiController]. L'esempio seguente illustra una classe di base personalizzata e un controller che deriva da tale classe:

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class PetsController : MyControllerBase

Attributo in un assembly

L'attributo [ApiController] può essere applicato a un assembly. Se l'attributo [ApiController] viene applicato a un assembly, tutti i controller al suo interno avranno l'attributo [ApiController] applicato. Non esiste alcun modo per rifiutare esplicitamente singoli controller. Applicare l'attributo a livello di assembly al file Program.cs:

using Microsoft.AspNetCore.Mvc;
[assembly: ApiController]

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Requisiti del routing degli attributi

Con l'attributo [ApiController] il routing degli attributi è un requisito. Ad esempio:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Le azioni non sono accessibili tramite le route convenzionali definite da UseEndpoints, UseMvc o UseMvcWithDefaultRoute.

Risposte HTTP 400 automatiche

Con l'attributo [ApiController] gli errori di convalida del modello attivano automaticamente una risposta HTTP 400. Di conseguenza è necessario il codice seguente in un metodo di azione:

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

ASP.NET Core MVC usa il filtro di azione ModelStateInvalidFilter per eseguire il controllo precedente.

Risposta BadRequest predefinita

Il corpo della risposta seguente è un esempio del tipo serializzato:

{
  "": [
    "A non-empty request body is required."
  ]
}

Il tipo di risposta predefinito per una risposta HTTP 400 è ValidationProblemDetails. Il corpo della risposta seguente è un esempio del tipo serializzato:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

Il tipo ValidationProblemDetails:

  • Fornisce un formato leggibile dal computer per specificare gli errori nelle risposte dell'API Web.
  • È conforme alla specifica RFC 7807.

Per rendere coerenti le risposte automatiche e personalizzate, chiamare il metodo ValidationProblem anziché BadRequest. ValidationProblem restituisce un oggetto ValidationProblemDetails oltre alla risposta automatica.

Registrare le risposte 400 automatiche

Per registrare le risposte 400 automatiche, impostare la proprietà delegata InvalidModelStateResponseFactory per eseguire l'elaborazione personalizzata. Per impostazione predefinita, InvalidModelStateResponseFactory usa ProblemDetailsFactory per creare un'istanza di ValidationProblemDetails.

L'esempio seguente illustra come recuperare un'istanza di ILogger<TCategoryName> per registrare informazioni su una risposta 400 automatica:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
      // To preserve the default behavior, capture the original delegate to call later.
        var builtInFactory = options.InvalidModelStateResponseFactory;

        options.InvalidModelStateResponseFactory = context =>
        {
            var logger = context.HttpContext.RequestServices
                                .GetRequiredService<ILogger<Program>>();

            // Perform logging here.
            // ...

            // Invoke the default behavior, which produces a ValidationProblemDetails
            // response.
            // To produce a custom response, return a different implementation of 
            // IActionResult instead.
            return builtInFactory(context);
        };
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Disabilitare la risposta 400 automatica

Per disabilitare il comportamento automatico per gli errori 400, impostare la proprietà SuppressModelStateInvalidFilter su true. Aggiungere il codice evidenziato seguente:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Inferenza del parametro di origine di associazione

Un attributo di origine di associazione definisce la posizione in cui viene trovato il valore del parametro di un'azione. Esistono gli attributi di origine di associazione seguente:

Attributo Origine di associazione
[FromBody] Corpo della richiesta
[FromForm] Dati di modulo nel corpo della richiesta
[FromHeader] Intestazione della richiesta
[FromQuery] Parametri della stringa di query della richiesta
[FromRoute] Dati della route della richiesta corrente
[FromServices] Il servizio richiesta inserito come parametro di azione

Avviso

Non usare [FromRoute] quando i valori potrebbero contenere %2f (vale a dire /). %2f non sarà convertito in / rimuovendo i caratteri di escape. Usare [FromQuery] se il valore potrebbe contenere %2f.

Senza l'attributo [ApiController] o altri attributi di origine di associazione come [FromQuery], il runtime di ASP.NET Core tenta di usare lo strumento di associazione di modelli a oggetti complesso. Lo strumento di associazione di modelli a oggetti complesso estrae i dati dal provider di valori in un ordine definito.

Nell'esempio seguente, l'attributo [FromQuery] indica che il valore del parametro discontinuedOnly è specificato nella stringa di query dell'URL della richiesta:

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

L'attributo [ApiController] applica le regole di inferenza per le origini dati predefinite dei parametri di azione. Queste regole consentono di evitare di dover identificare le origini di associazione manualmente applicando attributi ai parametri di azione. Il comportamento delle regole di inferenza per le origini di associazione è il seguente:

  • [FromBody] viene dedotto per parametri di tipo complessi non registrati nel contenitore di inserimento delle dipendenze. Un'eccezione alla regola di inferenza [FromBody] è costituita dai tipi predefiniti complessi con un significato speciale, ad esempio IFormCollection e CancellationToken. Il codice di inferenza di origine di associazione ignora tali tipi speciali.
  • [FromForm] viene dedotto per i parametri di azione di tipo IFormFile e IFormFileCollection. Non viene dedotto per i tipi semplici o definiti dall'utente.
  • [FromRoute] viene dedotto per i nomi di parametro di azione corrispondenti a un parametro nel modello di route. Quando più di una route corrisponde a un parametro di azione, tutti i valori di route vengono considerati [FromRoute].
  • [FromQuery] viene dedotto per tutti gli altri parametri di azione.

Note sulla regola di inferenza FromBody

[FromBody] non viene dedotto per i tipi semplici, ad esempio string o int. Pertanto, l'attributo [FromBody] deve essere usato per i tipi semplici se è necessaria tale funzionalità.

Quando a un'azione sono associati più parametri dal corpo della richiesta, viene generata un'eccezione. Tutte le firme di metodo di azione seguenti, ad esempio, causano un'eccezione:

  • [FromBody] dedotto per entrambi, perché si tratta di tipi complessi.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • [FromBody] attributo per uno, dedotto per l'altro perché è un tipo complesso.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • [FromBody] attributo per entrambi.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Disabilitare le regole di inferenza

Per disabilitare l'inferenza delle origini di associazione, impostare SuppressInferBindingSourcesForParameters su true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Inferenza di richieste multipart/form-data

L'attributo [ApiController] applica una regola di inferenza per i parametri di azione di tipo IFormFile e IFormFileCollection. Il tipo di contenuto della richiesta multipart/form-data viene dedotto per questi tipi.

Per disabilitare il comportamento predefinito, impostare la proprietà SuppressConsumesConstraintForFormFileParameters su true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Dettagli del problema per i codici di stato di errore

MVC trasforma un risultato di errore (un risultato con codice di stato 400 o superiore) in un risultato con ProblemDetails. Il tipo ProblemDetails si basa sulla specifica RFC 7807 per fornire dettagli sull'errore leggibili dal computer in una risposta HTTP.

Si consideri il codice seguente in un'azione del controller:

if (pet == null)
{
    return NotFound();
}

Il metodo NotFound produce un codice di stato HTTP 404 con un corpo ProblemDetails. Ad esempio:

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Disabilitare la risposta ProblemDetails

La creazione automatica di ProblemDetails per i codici di stato di errore è disabilitata quando la proprietà SuppressMapClientErrors è impostata su true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Definire i tipi di contenuto della richiesta supportati con l'attributo [Consumes]

Per impostazione predefinita, un'azione supporta tutti i tipi di contenuto della richiesta disponibili. Ad esempio, se un'app è configurata per supportare i formattatori di inputJSON e XML, un'azione supporta più tipi di contenuto, inclusi application/json e application/xml.

L'attributo [Consumes] consente a un'azione di limitare i tipi di contenuto della richiesta supportati. Applicare l'attributo [Consumes] a un'azione o a un controller, specificando uno o più tipi di contenuto:

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

Nel codice precedente l'azione CreateProduct specifica il tipo di contenuto application/xml. Le richieste instradate a questa azione devono specificare un'intestazione Content-Typeapplication/xml. Le richieste che non specificano un'intestazione Content-Typeapplication/xml generano una risposta 415 Tipo di supporto non supportato.

L'attributo [Consumes] consente a un'azione di influenzare la relativa selezione in base al tipo di contenuto di una richiesta in ingresso applicando un vincolo di tipo. Si consideri l'esempio seguente:

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

Nel codice precedente ConsumesController è configurato per gestire le richieste inviate all'URL https://localhost:5001/api/Consumes. Le azioni del controller PostJson e PostForm gestiscono le richieste POST con lo stesso URL. Senza l'attributo [Consumes] che applica un vincolo di tipo, viene generata un'eccezione di corrispondenza ambigua.

L'attributo [Consumes] viene applicato a entrambe le azioni. L'azione PostJson gestisce le richieste inviate con un'intestazione Content-Typeapplication/json. L'azione PostForm gestisce le richieste inviate con un'intestazione Content-Typeapplication/x-www-form-urlencoded.

Risorse aggiuntive

ASP.NET Core supporta la creazione di servizi RESTful, noti anche come API Web, con C#. Per gestire le richieste, un'API Web usa i controller. I controller in un'API Web sono classi che derivano da ControllerBase. Questo articolo illustra come usare i controller per gestire le richieste di API Web.

Visualizzare o scaricare il codice di esempio. (Come scaricare un esempio).

Classe ControllerBase

Un'API Web è costituita da una o più classi controller che derivano da ControllerBase. Il modello di progetto di API Web fornisce un controller iniziale:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Non creare un controller API Web tramite derivazione dalla classe Controller. La classe Controller deriva da ControllerBase e aggiunge il supporto per le visualizzazioni, pertanto è progettata per la gestione delle pagine Web e non per le richieste di API Web. Esiste un'eccezione a questa regola: se si prevede di usare lo stesso controller sia per le API Web che per le visualizzazioni, derivarlo da Controller.

La classe ControllerBase offre molti metodi e proprietà utili per la gestione delle richieste HTTP. Ad esempio, ControllerBase.CreatedAtAction restituisce un codice di stato 201:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

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

Di seguito sono elencati alcuni esempi di metodi forniti da ControllerBase.

Method Note
BadRequest Restituisce il codice di stato 400.
NotFound Restituisce il codice di stato 404.
PhysicalFile Restituisce un file.
TryUpdateModelAsync Richiama l'associazione di modelli.
TryValidateModel Richiama la convalida dei modelli.

Per un elenco di tutti i metodi e proprietà disponibili, vedere ControllerBase.

Attributi

Lo spazio dei nomi Microsoft.AspNetCore.Mvc include attributi che possono essere usati per configurare il comportamento del controller di API Web e i metodi di azione. L'esempio seguente usa attributi per specificare il verbo dell'azione HTTP supportato e tutti i codici di stato HTTP noti che potrebbero essere restituiti:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

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

Di seguito sono riportati altri esempi degli attributi disponibili.

Attributo Note
[Route] Specifica il modello di URL per un controller o un'azione.
[Bind] Specifica il prefisso e le proprietà da includere per l'associazione di modelli.
[HttpGet] Identifica un'azione che supporta il verbo dell'azione HTTP GET.
[Consumes] Specifica i tipi di dati accettati da un'azione.
[Produces] Specifica i tipi di dati restituiti da un'azione.

Per un elenco che include gli attributi disponibili, vedere lo spazio dei nomi Microsoft.AspNetCore.Mvc.

Attributo ApiController

L'attributo [ApiController] può essere applicato a una classe controller per abilitare i comportamenti opinionati specifici dell'API:

Attributo in controller specifici

L'attributo [ApiController] può essere applicato a controller specifici, come illustrato nell'esempio seguente dal modello di progetto:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Attributo in più controller

Un approccio per usare l'attributo su più di un controller consiste nel creare una classe di controller di base personalizzata annotata con l'attributo [ApiController]. L'esempio seguente illustra una classe di base personalizzata e un controller che deriva da tale classe:

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class PetsController : MyControllerBase

Attributo in un assembly

L'attributo [ApiController] può essere applicato a un assembly. L'uso delle annotazioni in questo modo applica il comportamento delle API Web a tutti i controller nell'assembly. Non esiste alcun modo per rifiutare esplicitamente singoli controller. Applicare l'attributo a livello di assembly alla dichiarazione dello spazio dei nomi che circonda la classe Startup:

[assembly: ApiController]
namespace WebApiSample
{
    public class Startup
    {
        ...
    }
}

Requisiti del routing degli attributi

Con l'attributo [ApiController] il routing degli attributi è un requisito. Ad esempio:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Le azioni non sono accessibili tramite le route convenzionali definite da UseEndpoints, UseMvc o UseMvcWithDefaultRoute in Startup.Configure.

Risposte HTTP 400 automatiche

Con l'attributo [ApiController] gli errori di convalida del modello attivano automaticamente una risposta HTTP 400. Di conseguenza è necessario il codice seguente in un metodo di azione:

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

ASP.NET Core MVC usa il filtro di azione ModelStateInvalidFilter per eseguire il controllo precedente.

Risposta BadRequest predefinita

Il corpo della richiesta seguente è un esempio del tipo serializzato:

{
  "": [
    "A non-empty request body is required."
  ]
}

Il tipo di risposta predefinito per una risposta HTTP 400 è ValidationProblemDetails. Il corpo della richiesta seguente è un esempio del tipo serializzato:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

Il tipo ValidationProblemDetails:

  • Fornisce un formato leggibile dal computer per specificare gli errori nelle risposte dell'API Web.
  • È conforme alla specifica RFC 7807.

Per rendere coerenti le risposte automatiche e personalizzate, chiamare il metodo ValidationProblem anziché BadRequest. ValidationProblem restituisce un oggetto ValidationProblemDetails oltre alla risposta automatica.

Registrare le risposte 400 automatiche

Per registrare le risposte 400 automatiche, impostare la proprietà delegata InvalidModelStateResponseFactory per eseguire l'elaborazione personalizzata in Startup.ConfigureServices. Per impostazione predefinita, InvalidModelStateResponseFactory usa ProblemDetailsFactory per creare un'istanza di ValidationProblemDetails.

L'esempio seguente illustra come recuperare un'istanza di ILogger<TCategoryName> per registrare informazioni su una risposta 400 automatica:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        // To preserve the default behavior, capture the original delegate to call later.
        var builtInFactory = options.InvalidModelStateResponseFactory;

        options.InvalidModelStateResponseFactory = context =>
        {
            var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<Startup>>();

            // Perform logging here.
            // ...

            // Invoke the default behavior, which produces a ValidationProblemDetails response.
            // To produce a custom response, return a different implementation of IActionResult instead.
            return builtInFactory(context);
        };
    });

Disabilitare la risposta 400 automatica

Per disabilitare il comportamento automatico per gli errori 400, impostare la proprietà SuppressModelStateInvalidFilter su true. Aggiungere il codice evidenziato seguente in Startup.ConfigureServices:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Inferenza del parametro di origine di associazione

Un attributo di origine di associazione definisce la posizione in cui viene trovato il valore del parametro di un'azione. Esistono gli attributi di origine di associazione seguente:

Attributo Origine di associazione
[FromBody] Corpo della richiesta
[FromForm] Dati di modulo nel corpo della richiesta
[FromHeader] Intestazione della richiesta
[FromQuery] Parametri della stringa di query della richiesta
[FromRoute] Dati della route della richiesta corrente
[FromServices] Il servizio richiesta inserito come parametro di azione

Avviso

Non usare [FromRoute] quando i valori potrebbero contenere %2f (vale a dire /). %2f non sarà convertito in / rimuovendo i caratteri di escape. Usare [FromQuery] se il valore potrebbe contenere %2f.

Senza l'attributo [ApiController] o altri attributi di origine di associazione come [FromQuery], il runtime di ASP.NET Core tenta di usare lo strumento di associazione di modelli a oggetti complesso. Lo strumento di associazione di modelli a oggetti complesso estrae i dati dal provider di valori in un ordine definito.

Nell'esempio seguente, l'attributo [FromQuery] indica che il valore del parametro discontinuedOnly è specificato nella stringa di query dell'URL della richiesta:

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

L'attributo [ApiController] applica le regole di inferenza per le origini dati predefinite dei parametri di azione. Queste regole consentono di evitare di dover identificare le origini di associazione manualmente applicando attributi ai parametri di azione. Il comportamento delle regole di inferenza per le origini di associazione è il seguente:

  • [FromBody] viene dedotto per i parametri di tipo complesso. Un'eccezione alla regola di inferenza [FromBody] è costituita dai tipi predefiniti complessi con un significato speciale, ad esempio IFormCollection e CancellationToken. Il codice di inferenza di origine di associazione ignora tali tipi speciali.
  • [FromForm] viene dedotto per i parametri di azione di tipo IFormFile e IFormFileCollection. Non viene dedotto per i tipi semplici o definiti dall'utente.
  • [FromRoute] viene dedotto per i nomi di parametro di azione corrispondenti a un parametro nel modello di route. Quando più di una route corrisponde a un parametro di azione, tutti i valori di route vengono considerati [FromRoute].
  • [FromQuery] viene dedotto per tutti gli altri parametri di azione.

Note sulla regola di inferenza FromBody

[FromBody] non viene dedotto per i tipi semplici, ad esempio string o int. Pertanto, l'attributo [FromBody] deve essere usato per i tipi semplici se è necessaria tale funzionalità.

Quando a un'azione sono associati più parametri dal corpo della richiesta, viene generata un'eccezione. Tutte le firme di metodo di azione seguenti, ad esempio, causano un'eccezione:

  • [FromBody] dedotto per entrambi, perché si tratta di tipi complessi.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • [FromBody] attributo per uno, dedotto per l'altro perché è un tipo complesso.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • [FromBody] attributo per entrambi.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Disabilitare le regole di inferenza

Per disabilitare l'inferenza delle origini di associazione, impostare SuppressInferBindingSourcesForParameters su true. Aggiungere il codice seguente a Startup.ConfigureServices:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Inferenza di richieste multipart/form-data

L'attributo [ApiController] applica una regola di inferenza per i parametri di azione di tipo IFormFile e IFormFileCollection. Il tipo di contenuto della richiesta multipart/form-data viene dedotto per questi tipi.

Per disabilitare il comportamento predefinito, impostare la proprietà SuppressConsumesConstraintForFormFileParameters su true in Startup.ConfigureServices:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Dettagli del problema per i codici di stato di errore

MVC trasforma un risultato di errore (un risultato con codice di stato 400 o superiore) in un risultato con ProblemDetails. Il tipo ProblemDetails si basa sulla specifica RFC 7807 per fornire dettagli sull'errore leggibili dal computer in una risposta HTTP.

Si consideri il codice seguente in un'azione del controller:

if (pet == null)
{
    return NotFound();
}

Il metodo NotFound produce un codice di stato HTTP 404 con un corpo ProblemDetails. Ad esempio:

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Disabilitare la risposta ProblemDetails

La creazione automatica di ProblemDetails per i codici di stato di errore è disabilitata quando la proprietà SuppressMapClientErrors è impostata su true. Aggiungere il codice seguente a Startup.ConfigureServices:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Definire i tipi di contenuto della richiesta supportati con l'attributo [Consumes]

Per impostazione predefinita, un'azione supporta tutti i tipi di contenuto della richiesta disponibili. Ad esempio, se un'app è configurata per supportare i formattatori di inputJSON e XML, un'azione supporta più tipi di contenuto, inclusi application/json e application/xml.

L'attributo [Consumes] consente a un'azione di limitare i tipi di contenuto della richiesta supportati. Applicare l'attributo [Consumes] a un'azione o a un controller, specificando uno o più tipi di contenuto:

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

Nel codice precedente l'azione CreateProduct specifica il tipo di contenuto application/xml. Le richieste instradate a questa azione devono specificare un'intestazione Content-Typeapplication/xml. Le richieste che non specificano un'intestazione Content-Typeapplication/xml generano una risposta 415 Tipo di supporto non supportato.

L'attributo [Consumes] consente a un'azione di influenzare la relativa selezione in base al tipo di contenuto di una richiesta in ingresso applicando un vincolo di tipo. Si consideri l'esempio seguente:

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

Nel codice precedente ConsumesController è configurato per gestire le richieste inviate all'URL https://localhost:5001/api/Consumes. Le azioni del controller PostJson e PostForm gestiscono le richieste POST con lo stesso URL. Senza l'attributo [Consumes] che applica un vincolo di tipo, viene generata un'eccezione di corrispondenza ambigua.

L'attributo [Consumes] viene applicato a entrambe le azioni. L'azione PostJson gestisce le richieste inviate con un'intestazione Content-Typeapplication/json. L'azione PostForm gestisce le richieste inviate con un'intestazione Content-Typeapplication/x-www-form-urlencoded.

Risorse aggiuntive

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase

Non creare un controller API Web tramite derivazione dalla classe Controller. La classe Controller deriva da ControllerBase e aggiunge il supporto per le visualizzazioni, pertanto è progettata per la gestione delle pagine Web e non per le richieste di API Web. Esiste un'eccezione a questa regola: se si prevede di usare lo stesso controller sia per le API Web che per le visualizzazioni, derivarlo da Controller. La classe ControllerBase offre molti metodi e proprietà utili per la gestione delle richieste HTTP. Ad esempio, ControllerBase.CreatedAtAction restituisce un codice di stato 201:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

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

Ecco alcuni altri esempi di metodi forniti da ControllerBase:

Method Note
BadRequest Restituisce il codice di stato 400.
NotFound Restituisce il codice di stato 404.
PhysicalFile Restituisce un file.
TryUpdateModelAsync Richiama l'associazione di modelli.
TryValidateModel Richiama la convalida dei modelli.

Per un elenco di tutti i metodi e proprietà disponibili, vedere ControllerBase.

Attributi

Lo spazio dei nomi Microsoft.AspNetCore.Mvc include attributi che possono essere usati per configurare il comportamento del controller di API Web e i metodi di azione. L'esempio seguente usa attributi per specificare il verbo dell'azione HTTP supportato e tutti i codici di stato HTTP noti che potrebbero essere restituiti:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

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

Ecco alcuni altri esempi di attributi disponibili:

Attributo Note
[Route] Specifica il modello di URL per un controller o un'azione.
[Bind] Specifica il prefisso e le proprietà da includere per l'associazione di modelli.
[HttpGet] Identifica un'azione che supporta il verbo dell'azione HTTP GET.
[Consumes] Specifica i tipi di dati accettati da un'azione.
[Produces] Specifica i tipi di dati restituiti da un'azione.

Per un elenco che include gli attributi disponibili, vedere lo spazio dei nomi Microsoft.AspNetCore.Mvc.

Attributo ApiController

L'attributo [ApiController] può essere applicato a una classe controller per abilitare i comportamenti opinionati specifici dell'API:

Attributo in controller specifici

L'attributo [ApiController] può essere applicato a controller specifici, come illustrato nell'esempio seguente dal modello di progetto:

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase

Attributo in più controller

Un approccio per usare l'attributo su più di un controller consiste nel creare una classe di controller di base personalizzata annotata con l'attributo [ApiController]. L'esempio seguente illustra una classe di base personalizzata e un controller che deriva da tale classe:

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("api/[controller]")]
public class PetsController : MyControllerBase

Attributo in un assembly

Se la versione di compatibilità è impostata su 2.2 o una versione successiva, l'attributo [ApiController] può essere applicato a un assembly. L'uso delle annotazioni in questo modo applica il comportamento delle API Web a tutti i controller nell'assembly. Non esiste alcun modo per rifiutare esplicitamente singoli controller. Applicare l'attributo a livello di assembly alla dichiarazione dello spazio dei nomi che circonda la classe Startup:

[assembly: ApiController]
namespace WebApiSample
{
    public class Startup
    {
        ...
    }
}

Requisiti del routing degli attributi

Con l'attributo [ApiController] il routing degli attributi è un requisito. Ad esempio:

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase

Le azioni non sono accessibili tramite le route convenzionali definite da UseMvc o UseMvcWithDefaultRoute in Startup.Configure.

Risposte HTTP 400 automatiche

Con l'attributo [ApiController] gli errori di convalida del modello attivano automaticamente una risposta HTTP 400. Di conseguenza è necessario il codice seguente in un metodo di azione:

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

ASP.NET Core MVC usa il filtro di azione ModelStateInvalidFilter per eseguire il controllo precedente.

Risposta BadRequest predefinita

Con una versione di compatibilità 2.1, il tipo di risposta predefinito per le risposte HTTP 400 è SerializableError. Il corpo della richiesta seguente è un esempio del tipo serializzato:

{
  "": [
    "A non-empty request body is required."
  ]
}

Con una versione di compatibilità 2.2 o successiva, il tipo di risposta predefinito per le risposte HTTP 400 è ValidationProblemDetails. Il corpo della richiesta seguente è un esempio del tipo serializzato:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

Il tipo ValidationProblemDetails:

  • Fornisce un formato leggibile dal computer per specificare gli errori nelle risposte dell'API Web.
  • È conforme alla specifica RFC 7807.

Per rendere coerenti le risposte automatiche e personalizzate, chiamare il metodo ValidationProblem anziché BadRequest. ValidationProblem restituisce un oggetto ValidationProblemDetails oltre alla risposta automatica.

Registrare le risposte 400 automatiche

Vedere Come registrare le risposte 400 automatiche negli errori di convalida del modello (aspnet/AspNetCore.Docs 12157).

Disabilitare la risposta 400 automatica

Per disabilitare il comportamento automatico per gli errori 400, impostare la proprietà SuppressModelStateInvalidFilter su true. Aggiungere il codice evidenziato seguente in Startup.ConfigureServices:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

Inferenza del parametro di origine di associazione

Un attributo di origine di associazione definisce la posizione in cui viene trovato il valore del parametro di un'azione. Esistono gli attributi di origine di associazione seguente:

Attributo Origine di associazione
[FromBody] Corpo della richiesta
[FromForm] Dati di modulo nel corpo della richiesta
[FromHeader] Intestazione della richiesta
[FromQuery] Parametri della stringa di query della richiesta
[FromRoute] Dati della route della richiesta corrente
[FromServices] Il servizio richiesta inserito come parametro di azione

Avviso

Non usare [FromRoute] quando i valori potrebbero contenere %2f (vale a dire /). %2f non sarà convertito in / rimuovendo i caratteri di escape. Usare [FromQuery] se il valore potrebbe contenere %2f. Senza l'attributo [ApiController] o altri attributi di origine di associazione come [FromQuery], il runtime di ASP.NET Core tenta di usare lo strumento di associazione di modelli a oggetti complesso. Lo strumento di associazione di modelli a oggetti complesso estrae i dati dal provider di valori in un ordine definito.

Nell'esempio seguente, l'attributo [FromQuery] indica che il valore del parametro discontinuedOnly è specificato nella stringa di query dell'URL della richiesta:

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

L'attributo [ApiController] applica le regole di inferenza per le origini dati predefinite dei parametri di azione. Queste regole consentono di evitare di dover identificare le origini di associazione manualmente applicando attributi ai parametri di azione. Il comportamento delle regole di inferenza per le origini di associazione è il seguente:

  • [FromBody] viene dedotto per i parametri di tipo complesso. Un'eccezione alla regola di inferenza [FromBody] è costituita dai tipi predefiniti complessi con un significato speciale, ad esempio IFormCollection e CancellationToken. Il codice di inferenza di origine di associazione ignora tali tipi speciali.
  • [FromForm] viene dedotto per i parametri di azione di tipo IFormFile e IFormFileCollection. Non viene dedotto per i tipi semplici o definiti dall'utente.
  • [FromRoute] viene dedotto per i nomi di parametro di azione corrispondenti a un parametro nel modello di route. Quando più di una route corrisponde a un parametro di azione, tutti i valori di route vengono considerati [FromRoute].
  • [FromQuery] viene dedotto per tutti gli altri parametri di azione.

Note sulla regola di inferenza FromBody

[FromBody] non viene dedotto per i tipi semplici, ad esempio string o int. Pertanto, l'attributo [FromBody] deve essere usato per i tipi semplici se è necessaria tale funzionalità.

Quando a un'azione sono associati più parametri dal corpo della richiesta, viene generata un'eccezione. Tutte le firme di metodo di azione seguenti, ad esempio, causano un'eccezione:

  • [FromBody] dedotto per entrambi, perché si tratta di tipi complessi.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • [FromBody] attributo per uno, dedotto per l'altro perché è un tipo complesso.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • [FromBody] attributo per entrambi.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Nota

In ASP.NET Core 2.1, i parametri di tipo raccolta, come elenchi e matrici, vengono dedotti in modo errato come [FromQuery]. È necessario usare l'attributo [FromBody] per questi parametri, se devono essere associati dal corpo della richiesta. Questo comportamento è stato corretto in ASP.NET Core 2.2 o versioni successive, in cui i parametri di tipo raccolta vengono dedotti come associati dal corpo per impostazione predefinita.

Disabilitare le regole di inferenza

Per disabilitare l'inferenza delle origini di associazione, impostare SuppressInferBindingSourcesForParameters su true. Aggiungere il codice seguente a Startup.ConfigureServices:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

Inferenza di richieste multipart/form-data

L'attributo [ApiController] applica una regola di inferenza per i parametri di azione di tipo IFormFile e IFormFileCollection. Il tipo di contenuto della richiesta multipart/form-data viene dedotto per questi tipi. Per disabilitare il comportamento predefinito, impostare la proprietà SuppressConsumesConstraintForFormFileParameters su true in Startup.ConfigureServices:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

Dettagli del problema per i codici di stato di errore

Con la versione di compatibilità 2.2 o successiva, MVC consente di trasformare un risultato di errore (un risultato con codice di stato 400 o superiore) in un risultato con ProblemDetails. Il tipo ProblemDetails si basa sulla specifica RFC 7807 per fornire dettagli sull'errore leggibili dal computer in una risposta HTTP. Si consideri il codice seguente in un'azione del controller:

if (pet == null)
{
    return NotFound();
}

Il metodo NotFound produce un codice di stato HTTP 404 con un corpo ProblemDetails. Ad esempio:

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Disabilitare la risposta ProblemDetails

La creazione automatica di ProblemDetails per i codici di stato di errore è disabilitata quando la proprietà SuppressMapClientErrors è impostata su true. Aggiungere il codice seguente a Startup.ConfigureServices:

Definire i tipi di contenuto della richiesta supportati con l'attributo [Consumes]

Per impostazione predefinita, un'azione supporta tutti i tipi di contenuto della richiesta disponibili. Ad esempio, se un'app è configurata per supportare i formattatori di inputJSON e XML, un'azione supporta più tipi di contenuto, inclusi application/json e application/xml.

L'attributo [Consumes] consente a un'azione di limitare i tipi di contenuto della richiesta supportati. Applicare l'attributo [Consumes] a un'azione o a un controller, specificando uno o più tipi di contenuto:

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

Nel codice precedente l'azione CreateProduct specifica il tipo di contenuto application/xml. Le richieste instradate a questa azione devono specificare un'intestazione Content-Typeapplication/xml. Le richieste che non specificano un'intestazione Content-Typeapplication/xml generano una risposta 415 Tipo di supporto non supportato. L'attributo [Consumes] consente a un'azione di influenzare la relativa selezione in base al tipo di contenuto di una richiesta in ingresso applicando un vincolo di tipo. Si consideri l'esempio seguente:

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

Nel codice precedente ConsumesController è configurato per gestire le richieste inviate all'URL https://localhost:5001/api/Consumes. Le azioni del controller PostJson e PostForm gestiscono le richieste POST con lo stesso URL. Senza l'attributo [Consumes] che applica un vincolo di tipo, viene generata un'eccezione di corrispondenza ambigua. L'attributo [Consumes] viene applicato a entrambe le azioni. L'azione PostJson gestisce le richieste inviate con un'intestazione Content-Typeapplication/json. L'azione PostForm gestisce le richieste inviate con un'intestazione Content-Typeapplication/x-www-form-urlencoded.

Risorse aggiuntive