ASP.NET Web API 2의 미디어 포맷터

이 자습서에서는 ASP.NET Web API 추가 미디어 형식을 지원하는 방법을 보여 줍니다.

인터넷 미디어 유형

MIME 형식이라고도 하는 미디어 형식은 데이터 조각의 형식을 식별합니다. HTTP에서 미디어 형식은 메시지 본문의 형식을 설명합니다. 미디어 형식은 형식과 하위 형식의 두 문자열로 구성됩니다. 예:

  • 텍스트/html
  • image/png
  • application/json

HTTP 메시지에 엔터티 본문이 포함된 경우 Content-Type 헤더는 메시지 본문의 형식을 지정합니다. 이렇게 하면 받는 사람에게 메시지 본문의 내용을 구문 분석하는 방법을 알려줍니다.

예를 들어 HTTP 응답에 PNG 이미지가 포함된 경우 응답에는 다음 헤더가 있을 수 있습니다.

HTTP/1.1 200 OK
Content-Length: 95267
Content-Type: image/png

클라이언트가 요청 메시지를 보낼 때 Accept 헤더를 포함할 수 있습니다. Accept 헤더는 클라이언트가 서버에서 원하는 미디어 유형을 서버에 알려줍니다. 예:

Accept: text/html,application/xhtml+xml,application/xml

이 헤더는 클라이언트가 HTML, XHTML 또는 XML을 원한다는 것을 서버에 알려줍니다.

미디어 유형은 Web API가 HTTP 메시지 본문을 직렬화하고 역직렬화하는 방법을 결정합니다. Web API는 XML, JSON, BSON 및 form-urlencoded 데이터를 기본적으로 지원하며 미디어 포맷터를 작성하여 추가 미디어 형식을 지원할 수 있습니다.

미디어 포맷터를 만들려면 다음 클래스 중 하나에서 파생합니다.

  • MediaTypeFormatter. 이 클래스는 비동기 읽기 및 쓰기 메서드를 사용합니다.
  • BufferedMediaTypeFormatter. 이 클래스는 MediaTypeFormatter 에서 파생되지만 동기 읽기/쓰기 메서드를 사용합니다.

비동기 코드가 없으므로 BufferedMediaTypeFormatter 에서 파생하는 것이 더 간단하지만 I/O 중에 호출 스레드가 차단될 수 있음을 의미합니다.

예: CSV 미디어 포맷터 만들기

다음 예제에서는 Product 개체를 CSV(쉼표로 구분된 값) 형식으로 직렬화할 수 있는 미디어 형식 포맷터를 보여 줍니다. 이 예제에서는 CRUD 작업을 지원하는 웹 API 만들기 자습서에 정의된 제품 유형을 사용합니다. Product 개체의 정의는 다음과 같습니다.

namespace ProductStore.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

CSV 포맷터를 구현하려면 BufferedMediaTypeFormatter에서 파생되는 클래스를 정의합니다.

using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using ProductStore.Models;

namespace ProductStore.Formatters
{
    public class ProductCsvFormatter : BufferedMediaTypeFormatter
    {
    }
}

생성자에서 포맷터가 지원하는 미디어 형식을 추가합니다. 이 예제에서 포맷터는 단일 미디어 형식인 "text/csv"를 지원합니다.

public ProductCsvFormatter()
{
    // Add the supported media type.
    SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv"));
}

CanWriteType 메서드를 재정의하여 포맷터가 serialize할 수 있는 형식을 나타냅니다.

public override bool CanWriteType(System.Type type)
{
    if (type == typeof(Product))
    {
        return true;
    }
    else
    {
        Type enumerableType = typeof(IEnumerable<Product>);
        return enumerableType.IsAssignableFrom(type);
    }
}

이 예제에서 포맷터는 개체의 컬렉션뿐만 아니라 단일 Product 개체를 직렬화할 수 있습니다 Product .

마찬가지로 CanReadType 메서드를 재정의하여 포맷터가 역직렬화할 수 있는 형식을 나타냅니다. 이 예제에서 포맷터는 역직렬화를 지원하지 않으므로 메서드는 단순히 false를 반환 합니다.

public override bool CanReadType(Type type)
{
    return false;
}

마지막으로 WriteToStream 메서드를 재정의합니다. 이 메서드는 형식을 스트림에 기록하여 직렬화합니다. 포맷터가 역직렬화를 지원하는 경우 ReadFromStream 메서드도 재정의합니다.

public override void WriteToStream(Type type, object value, Stream writeStream, HttpContent content)
{
    using (var writer = new StreamWriter(writeStream))
    {
        var products = value as IEnumerable<Product>;
        if (products != null)
        {
            foreach (var product in products)
            {
                WriteItem(product, writer);
            }
        }
        else
        {
            var singleProduct = value as Product;
            if (singleProduct == null)
            {
                throw new InvalidOperationException("Cannot serialize type");
            }
            WriteItem(singleProduct, writer);
        }
    }
}

// Helper methods for serializing Products to CSV format. 
private void WriteItem(Product product, StreamWriter writer)
{
    writer.WriteLine("{0},{1},{2},{3}", Escape(product.Id),
        Escape(product.Name), Escape(product.Category), Escape(product.Price));
}

static char[] _specialChars = new char[] { ',', '\n', '\r', '"' };

private string Escape(object o)
{
    if (o == null)
    {
        return "";
    }
    string field = o.ToString();
    if (field.IndexOfAny(_specialChars) != -1)
    {
        // Delimit the entire field with quotes and replace embedded quotes with "".
        return String.Format("\"{0}\"", field.Replace("\"", "\"\""));
    }
    else return field;
}

Web API 파이프라인에 미디어 포맷터 추가

Web API 파이프라인에 미디어 형식 포맷터를 추가하려면 HttpConfiguration 개체의 Formatters 속성을 사용합니다.

public static void ConfigureApis(HttpConfiguration config)
{
    config.Formatters.Add(new ProductCsvFormatter()); 
}

문자 인코딩

필요에 따라 미디어 포맷터는 UTF-8 또는 ISO 8859-1과 같은 여러 문자 인코딩을 지원할 수 있습니다.

생성자에서 SupportedEncodings 컬렉션에 하나 이상의 System.Text.Encoding 형식을 추가합니다. 기본 인코딩을 먼저 배치합니다.

public ProductCsvFormatter()
{
    SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv"));

    // New code:
    SupportedEncodings.Add(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
    SupportedEncodings.Add(Encoding.GetEncoding("iso-8859-1"));
}

WriteToStreamReadFromStream 메서드에서 MediaTypeFormatter.SelectCharacterEncoding을 호출하여 기본 설정 문자 인코딩을 선택합니다. 이 메서드는 지원되는 인코딩 목록에 대한 요청 헤더와 일치합니다. 스트림에서 읽거나 쓸 때 반환 된 인코딩 을 사용합니다.

public override void WriteToStream(Type type, object value, Stream writeStream, HttpContent content)
{
    Encoding effectiveEncoding = SelectCharacterEncoding(content.Headers);

    using (var writer = new StreamWriter(writeStream, effectiveEncoding))
    {
        // Write the object (code not shown)
    }
}