Vlastní formátovací moduly ve webových rozhraních API ASP.NET Core
ASP.NET Core MVC podporuje výměnu dat ve webových rozhraních API pomocí vstupních a výstupních formátovacích nástrojů. Vstupní formátovací moduly používají vazby modelu. Výstupní formátovací moduly se používají k formátování odpovědí.
Tato architektura poskytuje integrované vstupní a výstupní formátovací moduly pro JSON a XML. Poskytuje integrovaný výstupní formátovací modul pro prostý text, ale neposkytuje vstupní formátovací modul pro prostý text.
Tento článek ukazuje, jak přidat podporu dalších formátů vytvořením vlastních formátovacích souborů. Příklad vlastního formátu formátu zadávání prostého textu najdete v tématu TextPlainInputFormatter na GitHubu.
Zobrazení nebo stažení ukázkového kódu (postup stažení)
Kdy použít vlastní formátovací modul
Pomocí vlastního formátovače můžete přidat podporu pro typ obsahu, který není zpracován integrovanými formátovacími moduly.
Přehled vytvoření vlastního formátovače
Vytvoření vlastního formátovače:
- Pro serializaci dat odesílaných klientovi vytvořte výstupní třídu formátovače.
- Pro deserializaci dat přijatých z klienta vytvořte vstupní formátovací třídu.
- Přidejte instance tříd formátovače do InputFormatters kolekcí OutputFormatters v .MvcOptions
Vytvoření vlastního formátovače
Vytvoření formátovače:
- Odvodit třídu z příslušné základní třídy. Ukázková aplikace je odvozena od a TextOutputFormatter TextInputFormatter.
- Zadejte podporované typy médií a kódování v konstruktoru.
- Přepište metody CanReadType a CanWriteType metody.
- Přepište metody ReadRequestBodyAsync a WriteResponseBodyAsync metody.
Následující kód ukazuje VcardOutputFormatter
třídu z ukázky:
public class VcardOutputFormatter : TextOutputFormatter
{
public VcardOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
protected override bool CanWriteType(Type? type)
=> typeof(Contact).IsAssignableFrom(type)
|| typeof(IEnumerable<Contact>).IsAssignableFrom(type);
public override async Task WriteResponseBodyAsync(
OutputFormatterWriteContext context, Encoding selectedEncoding)
{
var httpContext = context.HttpContext;
var serviceProvider = httpContext.RequestServices;
var logger = serviceProvider.GetRequiredService<ILogger<VcardOutputFormatter>>();
var buffer = new StringBuilder();
if (context.Object is IEnumerable<Contact> contacts)
{
foreach (var contact in contacts)
{
FormatVcard(buffer, contact, logger);
}
}
else
{
FormatVcard(buffer, (Contact)context.Object!, logger);
}
await httpContext.Response.WriteAsync(buffer.ToString(), selectedEncoding);
}
private static void FormatVcard(
StringBuilder buffer, Contact contact, ILogger logger)
{
buffer.AppendLine("BEGIN:VCARD");
buffer.AppendLine("VERSION:2.1");
buffer.AppendLine($"N:{contact.LastName};{contact.FirstName}");
buffer.AppendLine($"FN:{contact.FirstName} {contact.LastName}");
buffer.AppendLine($"UID:{contact.Id}");
buffer.AppendLine("END:VCARD");
logger.LogInformation("Writing {FirstName} {LastName}",
contact.FirstName, contact.LastName);
}
}
Odvození z příslušné základní třídy
Pro typy textových médií (například vCard) odvozujte ze TextInputFormatter základní třídy nebo TextOutputFormatter základní třídy:
public class VcardOutputFormatter : TextOutputFormatter
Binární typy jsou odvozeny od InputFormatter základní třídy.OutputFormatter
Určení podporovaných typů a kódování médií
V konstruktoru zadejte podporované typy médií a kódování přidáním do SupportedMediaTypes kolekcí:SupportedEncodings
public VcardOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
Formátovací třída nemůže pro své závislosti použít injektáž konstruktoru. Například ILogger<VcardOutputFormatter>
nelze do konstruktoru přidat jako parametr. Pro přístup ke službám použijte kontextový objekt, který se předá metodám. Příklad kódu v tomto článku a ukázka ukazuje, jak to udělat.
Přepsání canReadType a CanWriteType
Zadejte typ, který se má deserializovat do nebo serializovat z přepsáním CanReadType nebo CanWriteType metodami. Pokud chcete například vytvořit text vizitky Contact
vCard z typu a naopak:
protected override bool CanWriteType(Type? type)
=> typeof(Contact).IsAssignableFrom(type)
|| typeof(IEnumerable<Contact>).IsAssignableFrom(type);
Metoda CanWriteResult
V některých scénářích CanWriteResult je nutné přepsat místo CanWriteType. Použijte CanWriteResult
, pokud jsou splněny následující podmínky:
- Metoda akce vrátí třídu modelu.
- Existují odvozené třídy, které mohou být vráceny za běhu.
- Odvozená třída vrácená akcí musí být známa za běhu.
Předpokládejme například, že metoda akce:
- Podpis vrátí
Person
typ. - Může vrátit
Student
neboInstructor
typ, který je odvozen odPerson
.
Chcete-li formátovací modul zpracovat pouze Student
objekty, zkontrolujte typ Object v kontextovém objektu poskytnuté metodě CanWriteResult
. Když metoda akce vrátí IActionResult:
- Není nutné používat
CanWriteResult
. - Metoda
CanWriteType
obdrží typ modulu runtime.
Přepsání readRequestBodyAsync a WriteResponseBodyAsync
Deserializace nebo serializace se provádí v ReadRequestBodyAsync nebo WriteResponseBodyAsync. Následující příklad ukazuje, jak získat služby z kontejneru injektáže závislostí. Služby nelze získat z parametrů konstruktoru:
public override async Task WriteResponseBodyAsync(
OutputFormatterWriteContext context, Encoding selectedEncoding)
{
var httpContext = context.HttpContext;
var serviceProvider = httpContext.RequestServices;
var logger = serviceProvider.GetRequiredService<ILogger<VcardOutputFormatter>>();
var buffer = new StringBuilder();
if (context.Object is IEnumerable<Contact> contacts)
{
foreach (var contact in contacts)
{
FormatVcard(buffer, contact, logger);
}
}
else
{
FormatVcard(buffer, (Contact)context.Object!, logger);
}
await httpContext.Response.WriteAsync(buffer.ToString(), selectedEncoding);
}
private static void FormatVcard(
StringBuilder buffer, Contact contact, ILogger logger)
{
buffer.AppendLine("BEGIN:VCARD");
buffer.AppendLine("VERSION:2.1");
buffer.AppendLine($"N:{contact.LastName};{contact.FirstName}");
buffer.AppendLine($"FN:{contact.FirstName} {contact.LastName}");
buffer.AppendLine($"UID:{contact.Id}");
buffer.AppendLine("END:VCARD");
logger.LogInformation("Writing {FirstName} {LastName}",
contact.FirstName, contact.LastName);
}
Konfigurace MVC pro použití vlastního formátovače
Pokud chcete použít vlastní formátovací modul, přidejte instanci třídy formatter do MvcOptions.InputFormatters kolekce nebo MvcOptions.OutputFormatters kolekce:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.InputFormatters.Insert(0, new VcardInputFormatter());
options.OutputFormatters.Insert(0, new VcardOutputFormatter());
});
Formátovací moduly se vyhodnocují v pořadí, ve kterém má první přednost.
Úplná VcardInputFormatter
třída
Následující kód ukazuje VcardInputFormatter
třídu z ukázky:
public class VcardInputFormatter : TextInputFormatter
{
public VcardInputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
protected override bool CanReadType(Type type)
=> type == typeof(Contact);
public override async Task<InputFormatterResult> ReadRequestBodyAsync(
InputFormatterContext context, Encoding effectiveEncoding)
{
var httpContext = context.HttpContext;
var serviceProvider = httpContext.RequestServices;
var logger = serviceProvider.GetRequiredService<ILogger<VcardInputFormatter>>();
using var reader = new StreamReader(httpContext.Request.Body, effectiveEncoding);
string? nameLine = null;
try
{
await ReadLineAsync("BEGIN:VCARD", reader, context, logger);
await ReadLineAsync("VERSION:", reader, context, logger);
nameLine = await ReadLineAsync("N:", reader, context, logger);
var split = nameLine.Split(";".ToCharArray());
var contact = new Contact(FirstName: split[1], LastName: split[0].Substring(2));
await ReadLineAsync("FN:", reader, context, logger);
await ReadLineAsync("END:VCARD", reader, context, logger);
logger.LogInformation("nameLine = {nameLine}", nameLine);
return await InputFormatterResult.SuccessAsync(contact);
}
catch
{
logger.LogError("Read failed: nameLine = {nameLine}", nameLine);
return await InputFormatterResult.FailureAsync();
}
}
private static async Task<string> ReadLineAsync(
string expectedText, StreamReader reader, InputFormatterContext context,
ILogger logger)
{
var line = await reader.ReadLineAsync();
if (line is null || !line.StartsWith(expectedText))
{
var errorMessage = $"Looked for '{expectedText}' and got '{line}'";
context.ModelState.TryAddModelError(context.ModelName, errorMessage);
logger.LogError(errorMessage);
throw new Exception(errorMessage);
}
return line;
}
}
Otestování aplikace
Spusťte ukázkovou aplikaci pro tento článek, která implementuje základní vstupní a výstupní formátovací moduly vCard. Aplikace čte a zapisuje vizitky vCard podobně jako v následujícím formátu:
BEGIN:VCARD
VERSION:2.1
N:Davolio;Nancy
FN:Nancy Davolio
END:VCARD
Pokud chcete zobrazit výstup vizitky vCard, spusťte aplikaci a odešlete požadavek Get s hlavičkou text/vcard
Accept na https://localhost:<port>/api/contacts
adresu .
Přidání vizitky vCard do kolekce kontaktů v paměti:
Post
Odešlete požadavek/api/contacts
pomocí nástroje, jako je http-repl.Content-Type
Nastavte záhlaví natext/vcard
hodnotu .- Nastavte
vCard
text v textu naformátovaný jako předchozí příklad.
Další materiály
ASP.NET Core MVC podporuje výměnu dat ve webových rozhraních API pomocí vstupních a výstupních formátovacích nástrojů. Vstupní formátovací moduly používají vazby modelu. Výstupní formátovací moduly se používají k formátování odpovědí.
Tato architektura poskytuje integrované vstupní a výstupní formátovací moduly pro JSON a XML. Poskytuje integrovaný výstupní formátovací modul pro prostý text, ale neposkytuje vstupní formátovací modul pro prostý text.
Tento článek ukazuje, jak přidat podporu dalších formátů vytvořením vlastních formátovacích souborů. Příklad vlastního formátu formátu zadávání prostého textu najdete v tématu TextPlainInputFormatter na GitHubu.
Zobrazení nebo stažení ukázkového kódu (postup stažení)
Kdy použít vlastní formátovací modul
Pomocí vlastního formátovače můžete přidat podporu pro typ obsahu, který není zpracován integrovanými formátovacími moduly.
Přehled vytvoření vlastního formátovače
Vytvoření vlastního formátovače:
- Pro serializaci dat odesílaných klientovi vytvořte výstupní třídu formátovače.
- Pro deserializaci dat přijatých z klienta vytvořte vstupní formátovací třídu.
- Přidejte instance tříd formátovače do InputFormatters kolekcí OutputFormatters v .MvcOptions
Vytvoření vlastního formátovače
Vytvoření formátovače:
- Odvodit třídu z příslušné základní třídy. Ukázková aplikace je odvozena od a TextOutputFormatter TextInputFormatter.
- Zadejte podporované typy médií a kódování v konstruktoru.
- Přepište metody CanReadType a CanWriteType metody.
- Přepište metody ReadRequestBodyAsync a WriteResponseBodyAsync metody.
Následující kód ukazuje VcardOutputFormatter
třídu z ukázky:
public class VcardOutputFormatter : TextOutputFormatter
{
public VcardOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
protected override bool CanWriteType(Type type)
{
return typeof(Contact).IsAssignableFrom(type) ||
typeof(IEnumerable<Contact>).IsAssignableFrom(type);
}
public override async Task WriteResponseBodyAsync(
OutputFormatterWriteContext context, Encoding selectedEncoding)
{
var httpContext = context.HttpContext;
var serviceProvider = httpContext.RequestServices;
var logger = serviceProvider.GetRequiredService<ILogger<VcardOutputFormatter>>();
var buffer = new StringBuilder();
if (context.Object is IEnumerable<Contact> contacts)
{
foreach (var contact in contacts)
{
FormatVcard(buffer, contact, logger);
}
}
else
{
FormatVcard(buffer, (Contact)context.Object, logger);
}
await httpContext.Response.WriteAsync(buffer.ToString(), selectedEncoding);
}
private static void FormatVcard(
StringBuilder buffer, Contact contact, ILogger logger)
{
buffer.AppendLine("BEGIN:VCARD");
buffer.AppendLine("VERSION:2.1");
buffer.AppendLine($"N:{contact.LastName};{contact.FirstName}");
buffer.AppendLine($"FN:{contact.FirstName} {contact.LastName}");
buffer.AppendLine($"UID:{contact.Id}");
buffer.AppendLine("END:VCARD");
logger.LogInformation("Writing {FirstName} {LastName}",
contact.FirstName, contact.LastName);
}
}
Odvození z příslušné základní třídy
Pro typy textových médií (například vCard) odvozujte ze TextInputFormatter základní třídy nebo TextOutputFormatter základní třídy:
public class VcardOutputFormatter : TextOutputFormatter
Binární typy jsou odvozeny od InputFormatter základní třídy.OutputFormatter
Určení podporovaných typů a kódování médií
V konstruktoru zadejte podporované typy médií a kódování přidáním do SupportedMediaTypes kolekcí:SupportedEncodings
public VcardOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
Formátovací třída nemůže pro své závislosti použít injektáž konstruktoru. Například ILogger<VcardOutputFormatter>
nelze do konstruktoru přidat jako parametr. Pro přístup ke službám použijte kontextový objekt, který se předá metodám. Příklad kódu v tomto článku a ukázka ukazuje, jak to udělat.
Přepsání canReadType a CanWriteType
Zadejte typ, který se má deserializovat do nebo serializovat z přepsáním CanReadType nebo CanWriteType metodami. Pokud chcete například vytvořit text vizitky Contact
vCard z typu a naopak:
protected override bool CanWriteType(Type type)
{
return typeof(Contact).IsAssignableFrom(type) ||
typeof(IEnumerable<Contact>).IsAssignableFrom(type);
}
Metoda CanWriteResult
V některých scénářích CanWriteResult je nutné přepsat místo CanWriteType. Použijte CanWriteResult
, pokud jsou splněny následující podmínky:
- Metoda akce vrátí třídu modelu.
- Existují odvozené třídy, které mohou být vráceny za běhu.
- Odvozená třída vrácená akcí musí být známa za běhu.
Předpokládejme například, že metoda akce:
- Podpis vrátí
Person
typ. - Může vrátit
Student
neboInstructor
typ, který je odvozen odPerson
.
Chcete-li formátovací modul zpracovat pouze Student
objekty, zkontrolujte typ Object v kontextovém objektu poskytnuté metodě CanWriteResult
. Když metoda akce vrátí IActionResult:
- Není nutné používat
CanWriteResult
. - Metoda
CanWriteType
obdrží typ modulu runtime.
Přepsání readRequestBodyAsync a WriteResponseBodyAsync
Deserializace nebo serializace se provádí v ReadRequestBodyAsync nebo WriteResponseBodyAsync. Následující příklad ukazuje, jak získat služby z kontejneru injektáže závislostí. Služby nelze získat z parametrů konstruktoru:
public override async Task WriteResponseBodyAsync(
OutputFormatterWriteContext context, Encoding selectedEncoding)
{
var httpContext = context.HttpContext;
var serviceProvider = httpContext.RequestServices;
var logger = serviceProvider.GetRequiredService<ILogger<VcardOutputFormatter>>();
var buffer = new StringBuilder();
if (context.Object is IEnumerable<Contact> contacts)
{
foreach (var contact in contacts)
{
FormatVcard(buffer, contact, logger);
}
}
else
{
FormatVcard(buffer, (Contact)context.Object, logger);
}
await httpContext.Response.WriteAsync(buffer.ToString(), selectedEncoding);
}
private static void FormatVcard(
StringBuilder buffer, Contact contact, ILogger logger)
{
buffer.AppendLine("BEGIN:VCARD");
buffer.AppendLine("VERSION:2.1");
buffer.AppendLine($"N:{contact.LastName};{contact.FirstName}");
buffer.AppendLine($"FN:{contact.FirstName} {contact.LastName}");
buffer.AppendLine($"UID:{contact.Id}");
buffer.AppendLine("END:VCARD");
logger.LogInformation("Writing {FirstName} {LastName}",
contact.FirstName, contact.LastName);
}
Konfigurace MVC pro použití vlastního formátovače
Pokud chcete použít vlastní formátovací modul, přidejte instanci třídy formatter do MvcOptions.InputFormatters kolekce nebo MvcOptions.OutputFormatters kolekce:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
options.InputFormatters.Insert(0, new VcardInputFormatter());
options.OutputFormatters.Insert(0, new VcardOutputFormatter());
});
}
Formátovací moduly se vyhodnocují v pořadí, v jakém je vložíte. První má přednost.
Úplná VcardInputFormatter
třída
Následující kód ukazuje VcardInputFormatter
třídu z ukázky:
public class VcardInputFormatter : TextInputFormatter
{
public VcardInputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
protected override bool CanReadType(Type type)
{
return type == typeof(Contact);
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(
InputFormatterContext context, Encoding effectiveEncoding)
{
var httpContext = context.HttpContext;
var serviceProvider = httpContext.RequestServices;
var logger = serviceProvider.GetRequiredService<ILogger<VcardInputFormatter>>();
using var reader = new StreamReader(httpContext.Request.Body, effectiveEncoding);
string nameLine = null;
try
{
await ReadLineAsync("BEGIN:VCARD", reader, context, logger);
await ReadLineAsync("VERSION:", reader, context, logger);
nameLine = await ReadLineAsync("N:", reader, context, logger);
var split = nameLine.Split(";".ToCharArray());
var contact = new Contact
{
LastName = split[0].Substring(2),
FirstName = split[1]
};
await ReadLineAsync("FN:", reader, context, logger);
await ReadLineAsync("END:VCARD", reader, context, logger);
logger.LogInformation("nameLine = {nameLine}", nameLine);
return await InputFormatterResult.SuccessAsync(contact);
}
catch
{
logger.LogError("Read failed: nameLine = {nameLine}", nameLine);
return await InputFormatterResult.FailureAsync();
}
}
private static async Task<string> ReadLineAsync(
string expectedText, StreamReader reader, InputFormatterContext context,
ILogger logger)
{
var line = await reader.ReadLineAsync();
if (!line.StartsWith(expectedText))
{
var errorMessage = $"Looked for '{expectedText}' and got '{line}'";
context.ModelState.TryAddModelError(context.ModelName, errorMessage);
logger.LogError(errorMessage);
throw new Exception(errorMessage);
}
return line;
}
}
Otestování aplikace
Spusťte ukázkovou aplikaci pro tento článek, která implementuje základní vstupní a výstupní formátovací moduly vCard. Aplikace čte a zapisuje vizitky vCard podobně jako v následujícím formátu:
BEGIN:VCARD
VERSION:2.1
N:Davolio;Nancy
FN:Nancy Davolio
END:VCARD
Pokud chcete zobrazit výstup vizitky vCard, spusťte aplikaci a odešlete požadavek Get s hlavičkou text/vcard
Accept na https://localhost:5001/api/contacts
adresu .
Přidání vizitky vCard do kolekce kontaktů v paměti:
Post
Odešlete žádost/api/contacts
pomocí nástroje, jako je curl.Content-Type
Nastavte záhlaví natext/vcard
hodnotu .- Nastavte
vCard
text v textu naformátovaný jako předchozí příklad.