ASP.NET Core Web apı 'sindeki özel formatıcılar
, Kirk Larkabağı ve Tom Dykstratarafından yapılır.
ASP.NET Core MVC, giriş ve çıkış formatlayıcıları kullanılarak Web API 'Lerinde veri değişimini destekler. Giriş formatlayıcıları model bağlamatarafından kullanılır. Çıkış biçimleri, yanıtları biçimlendirmekiçin kullanılır.
Framework, JSON ve XML için yerleşik giriş ve çıkış biçimleri sağlar. Düz metin için yerleşik bir çıkış biçimlendiricisi sağlar, ancak düz metin için bir giriş biçimlendiricisi sağlamaz.
Bu makalede, özel formatlayıcılar oluşturarak ek biçimler için nasıl destek ekleneceği gösterilmektedir. Özel düz metin girişi biçimlendiricisi örneği için GitHub bkz. Textplainınputformatter .
Örnek kodu görüntüleme veya indirme (nasıl indirileceği)
Özel formatlayıcılar ne zaman kullanılır?
Yerleşik biçimleyiciler tarafından işlenmeyen bir içerik türü için destek eklemek üzere özel bir biçimlendirici kullanın.
Özel biçimlendirici kullanma konusuna genel bakış
Özel bir biçimlendirici oluşturmak için:
- İstemciye gönderilen verileri seri hale getirmek için bir çıkış biçimlendirici sınıfı oluşturun.
- İstemciden alınan verilerin serisini kaldırma için bir giriş biçimlendirici sınıfı oluşturun.
- İçindeki ve koleksiyonlarına biçimlendirici sınıf örnekleri ekleyin
InputFormattersOutputFormattersMvcOptions .
Özel bir biçimlendirici sınıfı oluşturma
Bir biçimlendirici oluşturmak için:
- Sınıfı uygun temel sınıftan türet. Örnek uygulama TextOutputFormatter ve ' den türetilir TextInputFormatter .
- Oluşturucuda geçerli medya türleri ve kodlamalar belirtin.
- CanReadTypeVe yöntemlerini geçersiz kılın CanWriteType .
- ReadRequestBodyAsyncVe yöntemlerini geçersiz kılın
WriteResponseBodyAsync.
Aşağıdaki kod, VcardOutputFormatter örnektensınıfını gösterir:
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);
}
}
Uygun taban sınıftan türet
Metin medya türleri için (örneğin, vCard), TextInputFormatter veya temel sınıftan türetirsiniz TextOutputFormatter .
public class VcardOutputFormatter : TextOutputFormatter
İkili türler için InputFormatter veya temel sınıftan türetirsiniz OutputFormatter .
Geçerli medya türlerini ve kodlamaları belirtin
Oluşturucuda, ve koleksiyonlarına ekleyerek geçerli medya türlerini ve kodlamaları belirtin SupportedMediaTypes SupportedEncodings .
public VcardOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
Bir biçimlendirici sınıfı, bağımlılıkları için Oluşturucu Ekleme işlemini kullanamaz. Örneğin, ILogger<VcardOutputFormatter> oluşturucuya bir parametre olarak eklenemez. Hizmetlere erişmek için yöntemlere geçirilen bağlam nesnesini kullanın. Bu makaledeki bir kod örneği ve örnek bunun nasıl yapılacağını göstermektedir.
CanReadType ve CanWriteType geçersiz kılınır
Veya yöntemlerini geçersiz kılarak içinden seri durumdan çıkarılacak veya seri hale getirilecek türü belirtin CanReadType CanWriteType . Örneğin, bir türden vCard metni oluşturma Contact ve tam tersi.
protected override bool CanWriteType(Type type)
{
return typeof(Contact).IsAssignableFrom(type) ||
typeof(IEnumerable<Contact>).IsAssignableFrom(type);
}
CanWriteResult yöntemi
Bazı senaryolarda, CanWriteResult yerine geçersiz kılınmalıdır CanWriteType . CanWriteResultAşağıdaki koşullar doğruysa kullanın:
- Eylem yöntemi bir model sınıfı döndürür.
- Çalışma zamanında döndürülebilecek türetilmiş sınıflar var.
- Eylem tarafından döndürülen türetilmiş sınıf, çalışma zamanında bilinmelidir.
Örneğin, eylem yöntemini varsayalım:
- İmza bir
Persontür döndürür. - ,
StudentInstructorÖğesinden türetilen bir veya türünü döndürebilirPerson.
Biçimlendiricisi yalnızca nesneleri işlemek için Student , Object metoduna sunulan bağlam nesnesindeki türünü denetleyin CanWriteResult . Eylem yöntemi şunu döndürdüğünde IActionResult :
- Kullanılması gerekli değildir
CanWriteResult. CanWriteTypeYöntemi, çalışma zamanı türünü alır.
ReadRequestBodyAsync ve WriteResponseBodyAsync geçersiz kıl
Seri durumdan çıkarma veya serileştirme gerçekleştirilir ReadRequestBodyAsync WriteResponseBodyAsync . Aşağıdaki örnek, bağımlılık ekleme kapsayıcısından hizmetlerin nasıl alınacağını gösterir. Hizmetler, Oluşturucu parametrelerinden elde alınamaz.
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);
}
MVC 'yi özel bir biçimlendirici kullanacak şekilde yapılandırma
Özel bir biçimlendirici kullanmak için, veya koleksiyonuna biçimlendirici sınıfının bir örneğini ekleyin InputFormatters OutputFormatters .
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
options.InputFormatters.Insert(0, new VcardInputFormatter());
options.OutputFormatters.Insert(0, new VcardOutputFormatter());
});
}
services.AddMvc(options =>
{
options.InputFormatters.Insert(0, new VcardInputFormatter());
options.OutputFormatters.Insert(0, new VcardOutputFormatter());
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
Biçimlendiriciler, eklediğiniz sırada değerlendirilir. Birincisi bir öncelik alır.
Tüm VcardInputFormatter sınıf
Aşağıdaki kod, VcardInputFormatter örnektensınıfını gösterir:
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;
}
}
Uygulamayı test etme
Bu makale için, temel vCard giriş ve çıkış formatlayıcıları uygulayan örnek uygulamayı çalıştırın. Uygulama, vCard 'ları aşağıdakine benzer şekilde okur ve yazar:
BEGIN:VCARD
VERSION:2.1
N:Davolio;Nancy
FN:Nancy Davolio
END:VCARD
VCard çıktısını görmek için, uygulamayı çalıştırın ve Accept üst bilgisine sahip bir get isteği gönderin text/vcard https://localhost:5001/api/contacts .
Bellek içi kişiler koleksiyonuna vCard eklemek için:
Post/api/contactsPostman gibi bir araçla bir istek gönderin.Content-TypeÜstbilgiyi olarak ayarlayıntext/vcard.vCardGövdedeki metni, önceki örnekte olduğu gibi ayarlayın.