Aracılığıyla paylaş


ASP.NET Core Web API'sindeki özel biçimlendiriciler

ASP.NET Core MVC, giriş ve çıkış biçimlendiricileri kullanarak Web API'lerinde veri alışverişini destekler. Giriş biçimlendiricileri Model Bağlama tarafından kullanılır. Çıkış biçimlendiricileri yanıtları biçimlendirmek için kullanılır.

Çerçeve, JSON ve XML için yerleşik giriş ve çıkış biçimlendiricileri sağlar. Düz metin için yerleşik bir çıkış biçimlendirici sağlar, ancak düz metin için giriş biçimlendiricisi sağlamaz.

Bu makalede, özel biçimlendiriciler oluşturarak ek biçimler için nasıl destek ekleneceği gösterilmektedir. Özel bir düz metin giriş biçimlendirici örneği için bkz . GitHub'da TextPlainInputFormatter .

Örnek kodu görüntüleme veya indirme (indirme)

Özel biçimlendirici ne zaman kullanılır?

Yerleşik biçimlendiriciler tarafından işlenmemiş bir içerik türü için destek eklemek için özel bir biçimlendirici kullanın.

Özel biçimlendirici oluşturmaya genel bakış

Özel 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 verileri seri durumdan çıkarmak için bir giriş biçimlendirici sınıfı oluşturun.
  • içindeki ve OutputFormatters koleksiyonlarına MvcOptionsbiçimlendirici sınıflarının InputFormatters örneklerini ekleyin.

Özel biçimlendirici oluşturma

Biçimlendirici oluşturmak için:

Aşağıdaki kod, örnekteki VcardOutputFormattersınıfı 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)
        => 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 temel sınıftan türet

Metin medyası türleri için (örneğin, vCard), veya TextOutputFormatter temel sınıfından TextInputFormatter türet:

public class VcardOutputFormatter : TextOutputFormatter

İkili türler için veya OutputFormatter temel sınıfından InputFormatter türetilir.

Desteklenen medya türlerini ve kodlamalarını belirtme

Oluşturucuda ve koleksiyonlarına ekleyerek SupportedMediaTypes desteklenen medya türlerini ve SupportedEncodings kodlamalarını belirtin:

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

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

Biçimlendirici sınıfı, bağımlılıkları için oluşturucu ekleme özelliğini kullanamaz. Örneğin, ILogger<VcardOutputFormatter> oluşturucuya 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österir.

CanReadType ve CanWriteType'ı Geçersiz Kılma

veya yöntemlerini geçersiz kılarak CanReadType CanWriteType içinde seri durumdan çıkarmak veya serileştirmek için türünü belirtin. Örneğin, bir Contact türden vCard metni oluşturmak için (veya tam tersi):

protected override bool CanWriteType(Type? type)
    => typeof(Contact).IsAssignableFrom(type)
        || typeof(IEnumerable<Contact>).IsAssignableFrom(type);

CanWriteResult yöntemi

Bazı senaryolarda yerine CanWriteResult geçersiz kılınmalıdır CanWriteType. Aşağıdaki koşullar doğruysa kullanın CanWriteResult :

  • 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 vardır.
  • Eylem tarafından döndürülen türetilmiş sınıfın çalışma zamanında bilinmesi gerekir.

Örneğin, eylem yöntemini varsayalım:

  • İmza bir Person tür döndürür.
  • türünden Persontüretilen bir Student veya Instructor türü döndürebilir.

Biçimlendiricinin yalnızca Student nesneleri işlemesi için yöntemine CanWriteResult sağlanan bağlam nesnesindeki türünü Object denetleyin. Eylem yöntemi döndürdüğünde IActionResult:

  • kullanmak CanWriteResultgerekli değildir.
  • yöntemi çalışma CanWriteType zamanı türünü alır.

ReadRequestBodyAsync ve WriteResponseBodyAsync'i geçersiz kılma

Seri durumdan çıkarma veya serileştirme veya veya WriteResponseBodyAsynciçinde ReadRequestBodyAsync gerçekleştirilir. Aşağıdaki örnekte, bağımlılık ekleme kapsayıcısından hizmetlerin nasıl alınacakları gösterilmektedir. Hizmetler oluşturucu parametrelerinden 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 biçimlendirici kullanacak şekilde yapılandırma

Özel bir biçimlendirici kullanmak için veya MvcOptions.OutputFormatters koleksiyonuna biçimlendirici sınıfının bir örneğini MvcOptions.InputFormatters ekleyin:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.InputFormatters.Insert(0, new VcardInputFormatter());
    options.OutputFormatters.Insert(0, new VcardOutputFormatter());
});

Biçimlendiriciler, ilkinin öncelikli olduğu eklenme sırasına göre değerlendirilir.

Tam VcardInputFormatter sınıf

Aşağıdaki kod, örnekteki VcardInputFormattersınıfı 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)
        => 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;
    }
}

Uygulamayı test etme

Temel vCard giriş ve çıkış biçimlendiricilerini uygulayan bu makale için örnek uygulamayı çalıştırın. Uygulama, vCard'ları aşağıdaki biçime benzer şekilde okur ve yazar:

BEGIN:VCARD
VERSION:2.1
N:Davolio;Nancy
FN:Nancy Davolio
END:VCARD

vCard çıkışını görmek için uygulamayı çalıştırın ve accept üst bilgisini text/vcard https://localhost:<port>/api/contactsiçeren bir Get isteği gönderin.

Kişilerden oluşan bellek içi koleksiyona vCard eklemek için:

  • Post http-repl gibi bir araçla adresine /api/contacts istek gönderin.
  • Content-Type Üst bilgiyi olarak text/vcardayarlayın.
  • Gövdedeki metni önceki örnekte olduğu gibi biçimlendirilmiş olarak ayarlayın vCard .

Ek kaynaklar

ASP.NET Core MVC, giriş ve çıkış biçimlendiricileri kullanarak Web API'lerinde veri alışverişini destekler. Giriş biçimlendiricileri Model Bağlama tarafından kullanılır. Çıkış biçimlendiricileri yanıtları biçimlendirmek için kullanılır.

Çerçeve, JSON ve XML için yerleşik giriş ve çıkış biçimlendiricileri sağlar. Düz metin için yerleşik bir çıkış biçimlendirici sağlar, ancak düz metin için giriş biçimlendiricisi sağlamaz.

Bu makalede, özel biçimlendiriciler oluşturarak ek biçimler için nasıl destek ekleneceği gösterilmektedir. Özel bir düz metin giriş biçimlendirici örneği için bkz . GitHub'da TextPlainInputFormatter .

Örnek kodu görüntüleme veya indirme (indirme)

Özel biçimlendirici ne zaman kullanılır?

Yerleşik biçimlendiriciler tarafından işlenmemiş bir içerik türü için destek eklemek için özel bir biçimlendirici kullanın.

Özel biçimlendirici oluşturmaya genel bakış

Özel 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 verileri seri durumdan çıkarmak için bir giriş biçimlendirici sınıfı oluşturun.
  • içindeki ve OutputFormatters koleksiyonlarına MvcOptionsbiçimlendirici sınıflarının InputFormatters örneklerini ekleyin.

Özel biçimlendirici oluşturma

Biçimlendirici oluşturmak için:

Aşağıdaki kod, örnekteki VcardOutputFormattersınıfı 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 temel sınıftan türet

Metin medyası türleri için (örneğin, vCard), veya TextOutputFormatter temel sınıfından TextInputFormatter türet:

public class VcardOutputFormatter : TextOutputFormatter

İkili türler için veya OutputFormatter temel sınıfından InputFormatter türetilir.

Desteklenen medya türlerini ve kodlamalarını belirtme

Oluşturucuda ve koleksiyonlarına ekleyerek SupportedMediaTypes desteklenen medya türlerini ve SupportedEncodings kodlamalarını belirtin:

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

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

Biçimlendirici sınıfı, bağımlılıkları için oluşturucu ekleme özelliğini kullanamaz. Örneğin, ILogger<VcardOutputFormatter> oluşturucuya 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österir.

CanReadType ve CanWriteType'ı Geçersiz Kılma

veya yöntemlerini geçersiz kılarak CanReadType CanWriteType içinde seri durumdan çıkarmak veya serileştirmek için türünü belirtin. Örneğin, bir Contact türden vCard metni oluşturmak için (veya tam tersi):

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

CanWriteResult yöntemi

Bazı senaryolarda yerine CanWriteResult geçersiz kılınmalıdır CanWriteType. Aşağıdaki koşullar doğruysa kullanın CanWriteResult :

  • 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 vardır.
  • Eylem tarafından döndürülen türetilmiş sınıfın çalışma zamanında bilinmesi gerekir.

Örneğin, eylem yöntemini varsayalım:

  • İmza bir Person tür döndürür.
  • türünden Persontüretilen bir Student veya Instructor türü döndürebilir.

Biçimlendiricinin yalnızca Student nesneleri işlemesi için yöntemine CanWriteResult sağlanan bağlam nesnesindeki türünü Object denetleyin. Eylem yöntemi döndürdüğünde IActionResult:

  • kullanmak CanWriteResultgerekli değildir.
  • yöntemi çalışma CanWriteType zamanı türünü alır.

ReadRequestBodyAsync ve WriteResponseBodyAsync'i geçersiz kılma

Seri durumdan çıkarma veya serileştirme veya veya WriteResponseBodyAsynciçinde ReadRequestBodyAsync gerçekleştirilir. Aşağıdaki örnekte, bağımlılık ekleme kapsayıcısından hizmetlerin nasıl alınacakları gösterilmektedir. Hizmetler oluşturucu parametrelerinden 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 biçimlendirici kullanacak şekilde yapılandırma

Özel bir biçimlendirici kullanmak için veya MvcOptions.OutputFormatters koleksiyonuna biçimlendirici sınıfının bir örneğini MvcOptions.InputFormatters ekleyin:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.InputFormatters.Insert(0, new VcardInputFormatter());
        options.OutputFormatters.Insert(0, new VcardOutputFormatter());
    });
}

Biçimlendiriciler, eklediğiniz sırayla değerlendirilir. İlki önceliklidir.

Tam VcardInputFormatter sınıf

Aşağıdaki kod, örnekteki VcardInputFormattersınıfı 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

Temel vCard giriş ve çıkış biçimlendiricilerini uygulayan bu makale için örnek uygulamayı çalıştırın. Uygulama, vCard'ları aşağıdaki biçime benzer şekilde okur ve yazar:

BEGIN:VCARD
VERSION:2.1
N:Davolio;Nancy
FN:Nancy Davolio
END:VCARD

vCard çıkışını görmek için uygulamayı çalıştırın ve accept üst bilgisini text/vcard https://localhost:5001/api/contactsiçeren bir Get isteği gönderin.

Kişilerden oluşan bellek içi koleksiyona vCard eklemek için:

  • Curl gibi bir Post araçla adresine /api/contacts istek gönderin.
  • Content-Type Üst bilgiyi olarak text/vcardayarlayın.
  • Gövdedeki metni önceki örnekte olduğu gibi biçimlendirilmiş olarak ayarlayın vCard .

Ek kaynaklar