Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
ASP.NET Core MVC stöder datautbyte i webb-API:er med hjälp av in- och utdataformaterare. Indataformaterare används av modellbindning. Utdataformaterare används för att formatera svar.
Ramverket tillhandahåller inbyggda in- och utdataformaterare för JSON och XML. Den innehåller en inbyggd utdataformaterare för oformaterad text, men ger ingen indataformaterare för oformaterad text.
Den här artikeln visar hur du lägger till stöd för ytterligare format genom att skapa anpassade formatters. Ett exempel på en anpassad formateringsformaterare för oformaterad text finns i TextPlainInputFormatter på GitHub.
Visa eller ladda ned exempelkod (hur du laddar ned)
När du ska använda en anpassad formatering
Använd en anpassad formatering för att lägga till stöd för en innehållstyp som inte hanteras av de inbyggda formatrarna.
Översikt över hur du skapar en anpassad formatterare
Så här skapar du en anpassad formatering:
- För serialisering av data som skickas till klienten skapar du en formateringsklass för utdata.
- För att deserialisera data som tas emot från klienten skapar du en indataformateringsklass.
- Lägg till instanser av formateringsklasser till samlingarna InputFormatters och OutputFormatters i MvcOptions.
Skapa en anpassad formatering
Så här skapar du en formaterare:
- Härled klassen från lämplig basklass. Exempelappen härleds från TextOutputFormatter och TextInputFormatter.
- Ange medietyper och kodningar som stöds i konstruktorn.
- Åsidosätt metoderna CanReadType och CanWriteType.
- Åsidosätt metoderna ReadRequestBodyAsync och WriteResponseBodyAsync.
Följande kod visar VcardOutputFormatter klassen från exemplet:
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);
}
}
Härled från lämplig basklass
För textmedietyper (till exempel vCard) härled från TextInputFormatter eller TextOutputFormatter basklassen:
public class VcardOutputFormatter : TextOutputFormatter
För binära typer härleds från basklassen InputFormatter eller OutputFormatter .
Ange medietyper och kodningar som stöds
I konstruktorn anger du medietyper och kodningar som stöds genom att lägga till i samlingarna SupportedMediaTypes och SupportedEncodings :
public VcardOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
En formateringsklass kan inte använda konstruktorinmatning för sina beroenden. Det går till exempel ILogger<VcardOutputFormatter> inte att lägga till som en parameter i konstruktorn. För att få åtkomst till tjänster använder du kontextobjektet som skickas till metoderna. Ett kodexempel i den här artikeln och exemplet visar hur du gör detta.
Åsidosätt CanReadType och CanWriteType
Ange vilken typ du vill deserialisera till eller serialisera från genom att åsidosätta metoderna CanReadType eller CanWriteType. Om du till exempel vill skapa vCard-text från en Contact typ och vice versa:
protected override bool CanWriteType(Type? type)
=> typeof(Contact).IsAssignableFrom(type)
|| typeof(IEnumerable<Contact>).IsAssignableFrom(type);
Metoden CanWriteResult
I vissa scenarier måste CanWriteResult åsidosättas istället för CanWriteType. Använd CanWriteResult om följande villkor är uppfyllda:
- Åtgärdsmetoden returnerar en modellklass.
- Det finns härledda klasser som kan returneras under körning.
- Den härledda klass som returneras av åtgärden måste vara känd vid körning.
Anta till exempel att åtgärdsmetoden:
- Signaturen returnerar en
Persondatatyp. - Kan returnera en
StudentellerInstructortyp som härleds frånPerson.
För att formateraren endast ska hantera Student objekt, kontrollera typen av Object i det kontextobjekt som tillhandahålls till CanWriteResult metoden. När åtgärdsmetoden returnerar IActionResult:
- Det är inte nödvändigt att använda
CanWriteResult. - Metoden
CanWriteTypetar emot körningstypen.
Åsidosätt ReadRequestBodyAsync och WriteResponseBodyAsync
Deserialisering eller serialisering utförs i ReadRequestBodyAsync eller WriteResponseBodyAsync. I följande exempel visas hur du hämtar tjänster från containern för beroendeinjektion. Tjänster kan inte hämtas från konstruktorparametrar:
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);
}
Konfigurera MVC för att använda en anpassad formatering
Om du vill använda en anpassad formaterare lägger du till en instans av formateringsklassen MvcOptions.InputFormatters i eller MvcOptions.OutputFormatters -samlingen:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.InputFormatters.Insert(0, new VcardInputFormatter());
options.OutputFormatters.Insert(0, new VcardOutputFormatter());
});
Formaterare utvärderas i den ordning de infogas, och den första ges företräde.
Den fullständiga VcardInputFormatter klassen
Följande kod visar VcardInputFormatter klassen från exemplet:
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;
}
}
Testa appen
Kör exempelappen för den här artikeln, som implementerar grundläggande in- och utdataformaterare för virtuella kort. Appen läser och skriver visitkort som liknar följande format:
BEGIN:VCARD
VERSION:2.1
N:Davolio;Nancy
FN:Nancy Davolio
END:VCARD
Om du vill se utdata från ett visitkort kör du appen och skickar en Get-begäran med accepthuvud text/vcard till https://localhost:<port>/api/contacts.
Så här lägger du till ett virtuellt kort i den minnesinterna samlingen med kontakter:
- Skicka en
Postbegäran till/api/contactsmed ett verktyg som http-repl. - Ställ in
Content-Type-rubriken tilltext/vcard. - Ange
vCardtext i brödtexten, formaterad som föregående exempel.
Ytterligare resurser
ASP.NET Core MVC stöder datautbyte i webb-API:er med hjälp av in- och utdataformaterare. Indataformaterare används av modellbindning. Utdataformaterare används för att formatera svar.
Ramverket tillhandahåller inbyggda in- och utdataformaterare för JSON och XML. Den innehåller en inbyggd utdataformaterare för oformaterad text, men ger ingen indataformaterare för oformaterad text.
Den här artikeln visar hur du lägger till stöd för ytterligare format genom att skapa anpassade formatters. Ett exempel på en anpassad formateringsformaterare för oformaterad text finns i TextPlainInputFormatter på GitHub.
Visa eller ladda ned exempelkod (hur du laddar ned)
När du ska använda en anpassad formatering
Använd en anpassad formatering för att lägga till stöd för en innehållstyp som inte hanteras av de inbyggda formatrarna.
Översikt över hur du skapar en anpassad formatterare
Så här skapar du en anpassad formatering:
- För serialisering av data som skickas till klienten skapar du en formateringsklass för utdata.
- För att deserialisera data som tas emot från klienten skapar du en indataformateringsklass.
- Lägg till instanser av formateringsklasser till samlingarna InputFormatters och OutputFormatters i MvcOptions.
Skapa en anpassad formatering
Så här skapar du en formaterare:
- Härled klassen från lämplig basklass. Exempelappen härleds från TextOutputFormatter och TextInputFormatter.
- Ange medietyper och kodningar som stöds i konstruktorn.
- Åsidosätt metoderna CanReadType och CanWriteType.
- Åsidosätt metoderna ReadRequestBodyAsync och WriteResponseBodyAsync.
Följande kod visar VcardOutputFormatter klassen från exemplet:
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);
}
}
Härled från lämplig basklass
För textmedietyper (till exempel vCard) härled från TextInputFormatter eller TextOutputFormatter basklassen:
public class VcardOutputFormatter : TextOutputFormatter
För binära typer härleds från basklassen InputFormatter eller OutputFormatter .
Ange medietyper och kodningar som stöds
I konstruktorn anger du medietyper och kodningar som stöds genom att lägga till i samlingarna SupportedMediaTypes och SupportedEncodings :
public VcardOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
En formateringsklass kan inte använda konstruktorinmatning för sina beroenden. Det går till exempel ILogger<VcardOutputFormatter> inte att lägga till som en parameter i konstruktorn. För att få åtkomst till tjänster använder du kontextobjektet som skickas till metoderna. Ett kodexempel i den här artikeln och exemplet visar hur du gör detta.
Åsidosätt CanReadType och CanWriteType
Ange vilken typ du vill deserialisera till eller serialisera från genom att åsidosätta metoderna CanReadType eller CanWriteType. Om du till exempel vill skapa vCard-text från en Contact typ och vice versa:
protected override bool CanWriteType(Type type)
{
return typeof(Contact).IsAssignableFrom(type) ||
typeof(IEnumerable<Contact>).IsAssignableFrom(type);
}
Metoden CanWriteResult
I vissa scenarier måste CanWriteResult åsidosättas istället för CanWriteType. Använd CanWriteResult om följande villkor är uppfyllda:
- Åtgärdsmetoden returnerar en modellklass.
- Det finns härledda klasser som kan returneras under körning.
- Den härledda klass som returneras av åtgärden måste vara känd vid körning.
Anta till exempel att åtgärdsmetoden:
- Signaturen returnerar en
Persondatatyp. - Kan returnera en
StudentellerInstructortyp som härleds frånPerson.
För att formateraren endast ska hantera Student objekt, kontrollera typen av Object i det kontextobjekt som tillhandahålls till CanWriteResult metoden. När åtgärdsmetoden returnerar IActionResult:
- Det är inte nödvändigt att använda
CanWriteResult. - Metoden
CanWriteTypetar emot körningstypen.
Åsidosätt ReadRequestBodyAsync och WriteResponseBodyAsync
Deserialisering eller serialisering utförs i ReadRequestBodyAsync eller WriteResponseBodyAsync. I följande exempel visas hur du hämtar tjänster från containern för beroendeinjektion. Tjänster kan inte hämtas från konstruktorparametrar:
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);
}
Konfigurera MVC för att använda en anpassad formatering
Om du vill använda en anpassad formaterare lägger du till en instans av formateringsklassen MvcOptions.InputFormatters i eller MvcOptions.OutputFormatters -samlingen:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
options.InputFormatters.Insert(0, new VcardInputFormatter());
options.OutputFormatters.Insert(0, new VcardOutputFormatter());
});
}
Formaterare utvärderas i den ordning du infogar dem. Den första har företräde.
Den fullständiga VcardInputFormatter klassen
Följande kod visar VcardInputFormatter klassen från exemplet:
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;
}
}
Testa appen
Kör exempelappen för den här artikeln, som implementerar grundläggande in- och utdataformaterare för virtuella kort. Appen läser och skriver visitkort som liknar följande format:
BEGIN:VCARD
VERSION:2.1
N:Davolio;Nancy
FN:Nancy Davolio
END:VCARD
Om du vill se utdata från ett visitkort kör du appen och skickar en Get-begäran med accepthuvud text/vcard till https://localhost:5001/api/contacts.
Så här lägger du till ett virtuellt kort i den minnesinterna samlingen med kontakter:
- Skicka en
Postbegäran till/api/contactsmed ett verktyg som curl. - Ställ in
Content-Type-rubriken tilltext/vcard. - Ange
vCardtext i brödtexten, formaterad som föregående exempel.
Ytterligare resurser
ASP.NET Core