web-dev-qa-db-fra.com

Comment implémenter une API Web ASP.NET Core "pure" en utilisant AddMvcCore ()

J'ai vu beaucoup de projets d'API Web ASP.NET Core qui utilisent le service AddMvc() par défaut sans se rendre compte que l'utilisation de AddMvcCore() est une option supérieure en raison du contrôle des services.

Comment implémentez-vous exactement une API Web ASP.NET Core en utilisant AddMvcCore() et pourquoi est-ce mieux?

33
Svek

Quelle est la différence entre AddMvc() et AddMvcCore()?

La première chose clé à comprendre est que AddMvc() est juste une version préchargée de AddMvcCore(). Vous pouvez voir l'implémentation exacte de l'extension AddMvc() dans le référentiel GitHub .

J'aime utiliser les modèles VS par défaut autant que le gars suivant, mais parfois vous devez savoir quand ce n'est pas le bon choix. J'ai vu plusieurs guides en ligne qui se penchent davantage sur une tentative de "défaire" ces services par défaut plutôt que de simplement aller avec une solution qui ne les met tout simplement pas en œuvre en premier lieu.

Avec l'avènement d'ASP.NET Core étant open source, il n'y a vraiment aucune bonne raison pour laquelle nous ne pouvons pas décoller une couche et travailler à un niveau inférieur sans craindre de perdre la "magie".


Définition de "minimal" et "pur"

Remarque: Les définitions sont destinées au contexte de cette réponse uniquement. Surtout par souci de clarté et pour aider à mieux comprendre.

Cette réponse penche davantage vers le "pur" et non le "minimal". J'aimerais expliquer pourquoi, c'est donc plus clair de quoi je parle.

Minimal. Une solution "minimale" serait une implémentation qui ne fait même pas appel à AddMvcCore() méthode du tout . La raison en est que MVC n'est pas vraiment un composant "requis" pour assembler votre propre API Web, et il ajoute certainement du poids à votre code avec les dépendances supplémentaires. Dans ce scénario, puisque vous n'utilisez pas la méthode AddMvcCore(), vous ne l'injecteriez pas non plus dans votre application, ici

public void Configure(IApplicationBuilder app)
{
    app.UseMvc(); // you don't need this
}

Cela signifierait mapper vos propres routes et répondre au context à votre manière. Ce n'est vraiment pas difficile du tout, mais je ne veux pas m'y plonger, car c'est assez hors sujet, mais voici un petit aperçu d'une implémentation minimale :

public void Configure(IApplicationBuilder app)
{
    app.Map("/api", HandleMapApi);
    // notice how we don't have app.UseMvc()?
}    

private static void HandleMapApi(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        // implement your own response
        await context.Response.WriteAsync("Hello WebAPI!");
    });
}

Pour de nombreux projets, une approche "minimale" signifie que nous abandonnons certaines des fonctionnalités de MVC. Vous devriez vraiment peser vos options et voir si vous ce chemin de conception est le bon choix, car il y a un équilibre entre le modèle de conception, la commodité, la maintenabilité, l'encombrement du code, et surtout les performances et la latence. Autrement dit: une solution "minimale" signifierait minimiser les services et middleware entre votre code et la demande.

Pure. Une solution "pure" (dans le contexte de cette réponse) consiste à éviter tous les services et middleware par défaut fournis "pré-groupés" "avec AddMvc() en ne l'implémentant pas en premier lieu. Au lieu de cela, nous utilisons AddMvcCore(), qui est expliqué plus en détail dans la section suivante:


Implémentation de nos propres services/middleware avec AddMvcCore()

La première chose à faire est de configurer ConfigureServices pour utiliser AddMvcCore(). Si vous regardez le référentiel GitHub , vous pouvez voir que AddMvc() appelle AddMvcCore() avec un ensemble standard de services/middleware:

Voici quelques-uns des services/middleware qui se distinguent comme "inutiles":

var builder = services.AddMvcCore();

builder.AddViews();
builder.AddRazorViewEngine();
builder.AddRazorPages();

Beaucoup de ces services par défaut sont parfaits pour un projet Web général, mais ne sont généralement pas souhaitables pour une API Web "pure".

Voici un exemple d'implémentation de ConfigureServices à l'aide de AddMvcCore() pour une API Web:

public void ConfigureServices(IServiceCollection services)
{
    // Build a customized MVC implementation, without using the default AddMvc(),
    // instead use AddMvcCore(). The repository link is below:
    // https://github.com/aspnet/Mvc/blob/release/2.2/src/Microsoft.AspNetCore.Mvc/MvcServiceCollectionExtensions.cs

    services
        .AddMvcCore(options =>
        {
            options.RequireHttpsPermanent = true; // this does not affect api requests
            options.RespectBrowserAcceptHeader = true; // false by default
            //options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();

            // these two are here to show you where to include custom formatters
            options.OutputFormatters.Add(new CustomOutputFormatter());
            options.InputFormatters.Add(new CustomInputFormatter());
        })
        //.AddApiExplorer()
        //.AddAuthorization()
        .AddFormatterMappings()
        //.AddCacheTagHelper()
        //.AddDataAnnotations()
        //.AddCors()
        .AddJsonFormatters();
}

L'implémentation ci-dessus est principalement un doublon de la méthode d'extension AddMvc(), cependant j'ai ajouté quelques nouvelles zones afin que d'autres puissent voir les avantages supplémentaires de le faire.

  • Formateurs d'entrée/sortie personnalisés. C'est ici que vous pouvez créer vos propres sérialiseurs hautement optimisés (tels que Protobuf, Thrift, Avro, Etc) au lieu d'utiliser JSON (ou pire XML) sérialisation.
  • Demander la gestion des en-têtes. Vous pouvez vous assurer que l'en-tête Accept est reconnu ou non.
  • Gestion des autorisations. Vous pouvez implémenter votre propre autorisation personnalisée ou profiter des fonctionnalités intégrées.
  • ApiExplorer. Pour certains projets, vous pouvez probablement l'inclure, sinon certains WebAPI peuvent ne pas vouloir cette fonctionnalité.
  • Demandes d'origine croisée (CORS). Si vous avez besoin d'une sécurité plus détendue sur votre WebAPI, vous pouvez l'activer.

Heureusement, avec cet exemple de solution "pure", vous pouvez voir les avantages de l'utilisation de AddMvcCore() et être à l'aise avec son utilisation.

Si vous êtes sérieux au sujet du contrôle des performances et de la latence tout en travaillant sur l'hôte Web d'ASP.NET Core, peut-être une plongée profonde dans une solution "minimale" est l'endroit où vous traitez directement au bord du pipeline de demande, plutôt que de laisser il s'enliser dans le middleware MVC.


Lecture supplémentaire

Un aperçu visuel de l'apparence du pipeline de middleware ... Selon mes définitions, moins de couches signifie "minimal", tandis que "pure" n'est qu'une version propre de MVC.

enter image description here

Vous pouvez en savoir plus à ce sujet sur les documents Microsoft: ASP.NET Core Middleware Fundamentals

75
Svek