ASP.NET Core: Media Formatter trong ASP.NET Web API
Hướng dẫn này cho thấy cách hỗ trợ các định dạng phương tiện (media format) bổ sung trong API Web ASP.NET.
Các loại phương tiện truyền thông Internet
Loại phương tiện (Media Type), còn được gọi là loại MIME, dùng để xác định định dạng (Format) của một phần dữ liệu. Trong HTTP, các loại phương tiện mô tả định dạng của nội dung thư. Một loại phương tiện bao gồm hai chuỗi, bao gồm một kiểu và một kiểu con. Ví dụ:
- text/html
- image/png
- application/json
Khi một thông báo HTTP chứa nội dung thực thể, thì header Content-Type sẽ chỉ định định dạng của nội dung thông báo. Điều này cho trình nhận (receiver) biết cách phân tích nội dung của phần thân của thông báo (message body).
Ví dụ: nếu phản hồi HTTP chứa hình ảnh PNG thì phản hồi có thể có các tiêu đề sau.
HTTP/1.1 200 OK
Content-Length: 95267
Content-Type: image/png
Khi máy khách gửi một tin nhắn yêu cầu, nó có thể bao gồm header Accept. Header Accept cho máy chủ biết (các) loại phương tiện nào mà máy khách muốn từ máy chủ. Ví dụ:
Accept: text/html,application/xhtml+xml,application/xml
Header này cho máy chủ biết rằng máy khách muốn HTML, XHTML hoặc XML.
Loại phương tiện xác định cách API Web tuần tự hóa và giải tuần tự hóa nội dung thông báo HTTP. API Web có hỗ trợ tích hợp cho dữ liệu XML, JSON, BSON và mã hóa url, đồng thời bạn có thể hỗ trợ các loại phương tiện bổ sung bằng cách viết trình media formatter.
Để tạo media formatter, hãy lấy một trong các lớp sau:
- MediaTypeFormatter. Lớp này sử dụng các phương thức đọc và ghi không đồng bộ.
- BufferedMediaTypeFormatter. Lớp này xuất phát từ MediaTypeFormatter nhưng sử dụng các phương thức đọc/ghi đồng bộ.
Sử dụng BufferedMediaTypeFormatter sẽ đơn giản hơn vì không có code không đồng bộ, nhưng điều đó cũng có nghĩa là luồng gọi có thể chặn trong I/O.
Ví dụ: Tạo Trình định dạng phương tiện CSV
Ví dụ sau đây cho thấy trình định dạng loại phương tiện có thể tuần tự hóa một đối tượng Product thành định dạng các giá trị được phân tách bằng dấu phẩy (CSV). Ví dụ này sử dụng loại Product được xác định trong hướng dẫn Tạo API Web hỗ trợ hoạt động CRUD. Dưới đây là định nghĩa của đối tượng 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; }
}
}
Để triển khai trình định dạng CSV, ta định nghĩa một lớp dẫn xuất từ 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
{
}
}
Trong hàm tạo, thêm các loại phương tiện mà trình định dạng hỗ trợ. Trong ví dụ này, trình định dạng hỗ trợ một loại phương tiện duy nhất là "text/csv":
public ProductCsvFormatter()
{
// Add the supported media type.
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv"));
}
Ghi đè phương thức CanWriteType để cho biết loại nào bộ định dạng có thể tuần tự hóa:
public override bool CanWriteType(System.Type type)
{
if (type == typeof(Product))
{
return true;
}
else
{
Type enumerableType = typeof(IEnumerable<Product>);
return enumerableType.IsAssignableFrom(type);
}
}
Trong ví dụ này, bộ định dạng có thể tuần tự hóa các đối tượng Product
đơn lẻ cũng như các collection đối tượng Product
.
Tương tự, ghi đè phương thức CanReadType để cho biết loại nào bộ định dạng có thể giải tuần tự hóa. Trong ví dụ này, trình định dạng không hỗ trợ quá trình giải tuần tự hóa nên phương thức này chỉ trả về false.
public override bool CanReadType(Type type)
{
return false;
}
Cuối cùng, ghi đè phương thức WriteToStream. Phương thức này tuần tự hóa một kiểu bằng cách ghi nó vào một luồng. Nếu trình định dạng của bạn hỗ trợ quá trình giải tuần tự hóa, hãy ghi đè phương thức 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;
}
Thêm Media Formatter vào Pipeline API Web
Để thêm trình định dạng loại phương tiện vào pipeline API Web, hãy sử dụng property Formatters trên đối tượng HttpConfiguration.
public static void ConfigureApis(HttpConfiguration config)
{
config.Formatters.Add(new ProductCsvFormatter());
}
Mã hóa ký tự
Tùy chọn, bộ định dạng phương tiện có thể hỗ trợ nhiều mã hóa ký tự, chẳng hạn như UTF-8 hoặc ISO 8859-1.
Trong hàm tạo, thêm một hoặc nhiều kiểu System.Text.Encoding vào collection SupportedEncodings. Đặt mã hóa mặc định đầu tiên.
public ProductCsvFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv"));
// New code:
SupportedEncodings.Add(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
SupportedEncodings.Add(Encoding.GetEncoding("iso-8859-1"));
}
Trong các phương thức WriteToStream và ReadFromStream, hãy gọi MediaTypeFormatter.SelectCharacterEncoding để chọn mã hóa ký tự ưa thích. Phương thức này khớp các header yêu cầu với danh sách mã hóa được hỗ trợ. Sử dụng Endcoding được trả về khi bạn đọc hoặc ghi từ luồng:
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)
}
}