Formatadores de mídia no ASP.NET Web API 2
Este tutorial mostra como dar suporte a formatos de mídia adicionais em ASP.NET Web API.
Tipos de mídia da Internet
Um tipo de mídia, também chamado de tipo MIME, identifica o formato de um pedaço de dados. Em HTTP, os tipos de mídia descrevem o formato do corpo da mensagem. Um tipo de mídia consiste em duas cadeias de caracteres, um tipo e um subtipo. Por exemplo:
- texto/html
- image/png
- aplicativo/json
Quando uma mensagem HTTP contém um corpo de entidade, o cabeçalho Content-Type especifica o formato do corpo da mensagem. Isso informa ao receptor como analisar o conteúdo do corpo da mensagem.
Por exemplo, se uma resposta HTTP contiver uma imagem PNG, a resposta poderá ter os cabeçalhos a seguir.
HTTP/1.1 200 OK
Content-Length: 95267
Content-Type: image/png
Quando o cliente envia uma mensagem de solicitação, ele pode incluir um cabeçalho Accept. O cabeçalho Accept informa ao servidor quais tipos de mídia o cliente deseja do servidor. Por exemplo:
Accept: text/html,application/xhtml+xml,application/xml
Esse cabeçalho informa ao servidor que o cliente deseja HTML, XHTML ou XML.
O tipo de mídia determina como a API Web serializa e desserializa o corpo da mensagem HTTP. A API Web tem suporte interno para dados XML, JSON, BSON e form-urlencoded, e você pode dar suporte a tipos de mídia adicionais escrevendo um formatador de mídia.
Para criar um formatador de mídia, derive de uma destas classes:
- MediaTypeFormatter. Essa classe usa métodos assíncronos de leitura e gravação.
- BufferedMediaTypeFormatter. Essa classe deriva de MediaTypeFormatter , mas usa métodos síncronos de leitura/gravação.
Derivar de BufferedMediaTypeFormatter é mais simples, porque não há código assíncrono, mas também significa que o thread de chamada pode ser bloqueado durante a E/S.
Exemplo: criando um formatador de mídia CSV
O exemplo a seguir mostra um formatador de tipo de mídia que pode serializar um objeto Product para um formato CSV (valores separados por vírgulas). Este exemplo usa o Tipo de produto definido no tutorial Criando uma API Web que dá suporte a operações CRUD. Esta é a definição do objeto 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; }
}
}
Para implementar um formatador CSV, defina uma classe derivada de 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
{
}
}
No construtor, adicione os tipos de mídia aos quais o formatador dá suporte. Neste exemplo, o formatador dá suporte a um único tipo de mídia, "text/csv":
public ProductCsvFormatter()
{
// Add the supported media type.
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv"));
}
Substitua o método CanWriteType para indicar quais tipos o formatador pode serializar:
public override bool CanWriteType(System.Type type)
{
if (type == typeof(Product))
{
return true;
}
else
{
Type enumerableType = typeof(IEnumerable<Product>);
return enumerableType.IsAssignableFrom(type);
}
}
Neste exemplo, o formatador pode serializar objetos individuais Product
, bem como coleções de Product
objetos.
Da mesma forma, substitua o método CanReadType para indicar quais tipos o formatador pode desserializar. Neste exemplo, o formatador não dá suporte à desserialização, portanto, o método simplesmente retorna false.
public override bool CanReadType(Type type)
{
return false;
}
Por fim, substitua o método WriteToStream . Esse método serializa um tipo gravando-o em um fluxo. Se o formatador der suporte à desserialização, substitua também o método 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;
}
Adicionando um formatador de mídia ao pipeline da API Web
Para adicionar um formatador de tipo de mídia ao pipeline da API Web, use a propriedade Formatters no objeto HttpConfiguration .
public static void ConfigureApis(HttpConfiguration config)
{
config.Formatters.Add(new ProductCsvFormatter());
}
Codificações de caracteres
Opcionalmente, um formatador de mídia pode dar suporte a várias codificações de caracteres, como UTF-8 ou ISO 8859-1.
No construtor, adicione um ou mais tipos System.Text.Encoding à coleção SupportedEncodings . Coloque a codificação padrão em primeiro lugar.
public ProductCsvFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv"));
// New code:
SupportedEncodings.Add(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
SupportedEncodings.Add(Encoding.GetEncoding("iso-8859-1"));
}
Nos métodos WriteToStream e ReadFromStream , chame MediaTypeFormatter.SelectCharacterEncoding para selecionar a codificação de caractere preferencial. Esse método corresponde aos cabeçalhos de solicitação em relação à lista de codificações com suporte. Use a Codificação retornada ao ler ou gravar no fluxo:
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)
}
}