Formateurs personnalisés dans l’API web ASP.NET CoreCustom formatters in ASP.NET Core Web API

Par Tom DykstraBy Tom Dykstra

ASP.NET Core MVC prend en charge l’échange de données dans les API web à l’aide des formats JSON ou XML.ASP.NET Core MVC has built-in support for data exchange in web APIs by using JSON or XML. Cet article montre comment ajouter la prise en charge de formats supplémentaires en créant des formateurs personnalisés.This article shows how to add support for additional formats by creating custom formatters.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)View or download sample code (how to download)

Quand utiliser les formateurs personnalisésWhen to use custom formatters

Utilisez un formateur personnalisé quand vous souhaitez que le processus de négociation de contenu prenne en charge un type de contenu non pris en charge par les formateurs intégrés (JSON et XML).Use a custom formatter when you want the content negotiation process to support a content type that isn't supported by the built-in formatters (JSON and XML).

Par exemple, si certains clients de votre API web peuvent prendre en charge le format Protobuf, vous pouvez être amené à utiliser Protobuf avec ces clients, car il est plus efficace.For example, if some of the clients for your web API can handle the Protobuf format, you might want to use Protobuf with those clients because it's more efficient. Vous pouvez également demander à votre API web d’envoyer les noms et adresses des contacts au format vCard, format couramment utilisé pour l’échange de données de contact.Or you might want your web API to send contact names and addresses in vCard format, a commonly used format for exchanging contact data. L’exemple d’application fourni dans cet article implémente un formateur vCard simple.The sample app provided with this article implements a simple vCard formatter.

Présentation de l’utilisation d’un formateur personnaliséOverview of how to use a custom formatter

Voici les étapes à suivre pour créer et utiliser un formateur personnalisé :Here are the steps to create and use a custom formatter:

  • Créez une classe de formateur de sortie si vous souhaitez sérialiser les données à envoyer au client.Create an output formatter class if you want to serialize data to send to the client.
  • Créez une classe de formateur d’entrée si vous souhaitez désérialiser les données reçues en provenance du client.Create an input formatter class if you want to deserialize data received from the client.
  • Ajoutez des instances de vos formateurs aux collections InputFormatters et OutputFormatters dans MvcOptions.Add instances of your formatters to the InputFormatters and OutputFormatters collections in MvcOptions.

Les sections suivantes fournissent de l’aide et des exemples de code pour chacune de ces étapes.The following sections provide guidance and code examples for each of these steps.

Guide pratique pour créer une classe de formateur personnaliséHow to create a custom formatter class

Pour créer un formateur :To create a formatter:

  • Faites dériver la classe de la classe de base appropriée.Derive the class from the appropriate base class.
  • Spécifiez les encodages et types de média valides dans le constructeur.Specify valid media types and encodings in the constructor.
  • Substituez les méthodes CanReadType/CanWriteType.Override CanReadType/CanWriteType methods
  • Substituez les méthodes ReadRequestBodyAsync/WriteResponseBodyAsync.Override ReadRequestBodyAsync/WriteResponseBodyAsync methods

Effectuer une dérivation à partir de la classe de base appropriéeDerive from the appropriate base class

Pour les médias de type texte (par exemple vCard), effectuez une dérivation à partir de la classe de base TextInputFormatter ou TextOutputFormatter.For text media types (for example, vCard), derive from the TextInputFormatter or TextOutputFormatter base class.

public class VcardOutputFormatter : TextOutputFormatter

Pour un exemple de formateur d’entrée, consultez l’exemple d’application.For an input formatter example, see the sample app.

Pour les types binaires, effectuez une dérivation à partir de la classe de base InputFormatter ou OutputFormatter.For binary types, derive from the InputFormatter or OutputFormatter base class.

Spécifier les encodages et types de média validesSpecify valid media types and encodings

Dans le constructeur, spécifiez les encodages et types de média valides en effectuant les ajouts nécessaires aux collections SupportedMediaTypes et SupportedEncodings.In the constructor, specify valid media types and encodings by adding to the SupportedMediaTypes and SupportedEncodings collections.

public VcardOutputFormatter()
{
    SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));

    SupportedEncodings.Add(Encoding.UTF8);
    SupportedEncodings.Add(Encoding.Unicode);
}

Pour un exemple de formateur d’entrée, consultez l’exemple d’application.For an input formatter example, see the sample app.

Notes

Vous ne pouvez pas effectuer d’injection de dépendances de constructeur dans une classe de formateur.You can't do constructor dependency injection in a formatter class. Par exemple, vous ne pouvez pas obtenir un journaliseur en ajoutant un paramètre de journaliseur au constructeur.For example, you can't get a logger by adding a logger parameter to the constructor. Pour accéder aux services, vous devez utiliser l’objet de contexte passé à vos méthodes.To access services, you have to use the context object that gets passed in to your methods. L’exemple de code ci-dessous montre comment procéder.A code example below shows how to do this.

Substituer CanReadType/CanWriteTypeOverride CanReadType/CanWriteType

Spécifiez le type cible de la désérialisation ou de la sérialisation en remplaçant les méthodes CanReadType ou CanWriteType.Specify the type you can deserialize into or serialize from by overriding the CanReadType or CanWriteType methods. Par exemple, vous pouvez peut-être créer uniquement un texte vCard à partir d’un type Contact, et inversement.For example, you might only be able to create vCard text from a Contact type and vice versa.

protected override bool CanWriteType(Type type)
{
    if (typeof(Contact).IsAssignableFrom(type) 
        || typeof(IEnumerable<Contact>).IsAssignableFrom(type))
    {
        return base.CanWriteType(type);
    }
    return false;
}

Pour un exemple de formateur d’entrée, consultez l’exemple d’application.For an input formatter example, see the sample app.

Méthode CanWriteResultThe CanWriteResult method

Dans certains cas, vous devez substituer CanWriteResult au lieu de CanWriteType.In some scenarios you have to override CanWriteResult instead of CanWriteType. Utilisez CanWriteResult si les conditions suivantes sont vraies :Use CanWriteResult if the following conditions are true:

  • Votre méthode d’action retourne une classe de modèle.Your action method returns a model class.
  • Il existe des classes dérivées qui peuvent être retournées au moment de l’exécution.There are derived classes which might be returned at runtime.
  • Vous devez savoir au moment de l’exécution quelle est la classe dérivée retournée par l’action.You need to know at runtime which derived class was returned by the action.

Par exemple, la signature de votre méthode d’action retourne un type Person, mais elle peut éventuellement retourner un type Student ou un type Instructor dérivé de Person.For example, suppose your action method signature returns a Person type, but it may return a Student or Instructor type that derives from Person. Si vous souhaitez que votre formateur gère uniquement les objets Student, vérifiez le type d’Object dans l’objet de contexte fourni à la méthode CanWriteResult.If you want your formatter to handle only Student objects, check the type of Object in the context object provided to the CanWriteResult method. Notez qu’il n’est pas nécessaire d’utiliser CanWriteResult quand la méthode d’action retourne IActionResult. Dans ce cas, la méthode CanWriteType reçoit le type au moment de l’exécution.Note that it's not necessary to use CanWriteResult when the action method returns IActionResult; in that case, the CanWriteType method receives the runtime type.

Substituer ReadRequestBodyAsync/WriteResponseBodyAsyncOverride ReadRequestBodyAsync/WriteResponseBodyAsync

Vous effectuez le travail réel de désérialisation ou de sérialisation dans ReadRequestBodyAsync ou WriteResponseBodyAsync.You do the actual work of deserializing or serializing in ReadRequestBodyAsync or WriteResponseBodyAsync. Les lignes surlignées dans l’exemple suivant montrent comment obtenir des services à partir du conteneur d’injection de dépendances (vous ne pouvez pas les obtenir à partir des paramètres du constructeur).The highlighted lines in the following example show how to get services from the dependency injection container (you can't get them from constructor parameters).

public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
    IServiceProvider serviceProvider = context.HttpContext.RequestServices;
    var logger = serviceProvider.GetService(typeof(ILogger<VcardOutputFormatter>)) as ILogger;

    var response = context.HttpContext.Response;

    var buffer = new StringBuilder();
    if (context.Object is IEnumerable<Contact>)
    {
        foreach (Contact contact in context.Object as IEnumerable<Contact>)
        {
            FormatVcard(buffer, contact, logger);
        }
    }
    else
    {
        var contact = context.Object as Contact;
        FormatVcard(buffer, contact, logger);
    }
    await response.WriteAsync(buffer.ToString());
}

private static void FormatVcard(StringBuilder buffer, Contact contact, ILogger logger)
{
    buffer.AppendLine("BEGIN:VCARD");
    buffer.AppendLine("VERSION:2.1");
    buffer.AppendFormat($"N:{contact.LastName};{contact.FirstName}\r\n");
    buffer.AppendFormat($"FN:{contact.FirstName} {contact.LastName}\r\n");
    buffer.AppendFormat($"UID:{contact.ID}\r\n");
    buffer.AppendLine("END:VCARD");
    logger.LogInformation($"Writing {contact.FirstName} {contact.LastName}");
}

Pour un exemple de formateur d’entrée, consultez l’exemple d’application.For an input formatter example, see the sample app.

Guide pratique pour configurer MVC et utiliser un formateur personnaliséHow to configure MVC to use a custom formatter

Pour utiliser un formateur personnalisé, ajoutez une instance de la classe du formateur à la collection InputFormatters ou OutputFormatters.To use a custom formatter, add an instance of the formatter class to the InputFormatters or OutputFormatters collection.

services.AddMvc(options =>
{
    options.InputFormatters.Insert(0, new VcardInputFormatter());
    options.OutputFormatters.Insert(0, new VcardOutputFormatter());
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

Les formateurs sont évalués dans l’ordre dans lequel vous les insérez.Formatters are evaluated in the order you insert them. Le premier est prioritaire.The first one takes precedence.

Étapes suivantesNext steps

BEGIN:VCARD
VERSION:2.1
N:Davolio;Nancy
FN:Nancy Davolio
UID:20293482-9240-4d68-b475-325df4a83728
END:VCARD

Pour afficher la sortie vCard, exécutez l’application et envoyez une requête Get avec « text/vcard » dans l’en-tête Accept à http://localhost:63313/api/contacts/ (quand l’exécution a lieu à partir de Visual Studio) ou http://localhost:5000/api/contacts/ (quand l’exécution a lieu à partir de la ligne de commande).To see vCard output, run the application and send a Get request with Accept header "text/vcard" to http://localhost:63313/api/contacts/ (when running from Visual Studio) or http://localhost:5000/api/contacts/ (when running from the command line).

Pour ajouter un vCard à la collection de contacts en mémoire, envoyez une requête Post à la même URL, avec « text/vcard » dans l’en-tête Content-Type et le texte du vCard dans le corps, comme dans l’exemple ci-dessus.To add a vCard to the in-memory collection of contacts, send a Post request to the same URL, with Content-Type header "text/vcard" and with vCard text in the body, formatted like the example above.