ASP.NET Web API 2 中的媒體格式器

本教學課程說明如何在 ASP.NET Web API 中支援其他媒體格式。

網際網路媒體類型

媒體類型也稱為 MIME 類型,可識別資料片段的格式。 在 HTTP 中,媒體類型會描述訊息本文的格式。 媒體類型是由兩個字串所組成:類型與子類型。 例如:

  • text/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 和表單 URL 編碼資料的內建支援,而且您可以撰寫 媒體格式器來支援其他媒體類型。

若要建立媒體格式器,請衍生自下列其中一個類別:

衍生自 BufferedMediaTypeFormatter 比較簡單,因為沒有非同步程式碼,但也表示呼叫執行緒可以在 I/O 期間封鎖。

範例:建立 CSV 媒體格式器

下列範例顯示可將 Product 物件序列化為逗號分隔值的媒體類型格式器, (CSV) 格式。 此範例會使用建立 支援 CRUD 作業的 Web API教學課程中定義的 Product 類型。 以下是 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 方法,以指出格式器可以序列化的類型:

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。

在建構函式中,將一或多個 System.Text.Encoding 類型新增至 SupportedEncodings 集合。 先放置預設編碼。

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)
    }
}