ASP.NET Web API 2 中的媒體格式器
本教學課程示範如何在 ASP.NET Web API 中支援其他媒體格式。
網路媒體類型
媒體類型,也稱為 MIME 類型,用於識別一段資料的格式。 在 HTTP 中,媒體類型描述了訊息本文的格式。 媒體類型由兩個字串組成,一個是類型 (type),另一個是子類型 (subtype)。 例如:
- 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 和表單 urlencoded 資料的支援,您可以透過撰寫媒體格式器來支援其他媒體類型。
若要建立媒體格式器,請從下列其中一個類別衍生:
- MediaTypeFormatter。 該類別使用非同步讀取和寫入方法。
- BufferedMediaTypeFormatter。 此類別衍生自 MediaTypeFormatter,但使用同步讀取/寫入方法。
從 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"));
}
在 WriteToStream 和 ReadFromStream 方法中,呼叫 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)
}
}