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 InputFormatters OutputFormatters MvcOptions .

Özel bir biçimlendirici sınıfı oluşturma

Bir biçimlendirici oluşturmak için:

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 Person tür döndürür.
  • , Student Instructor Öğesinden türetilen bir veya türünü döndürebilir Person .

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/contacts Postman gibi bir araçla bir istek gönderin.
  • Content-TypeÜstbilgiyi olarak ayarlayın text/vcard .
  • vCardGövdedeki metni, önceki örnekte olduğu gibi ayarlayın.

Ek kaynaklar