Форматировщики медиа-типов

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core

Последнее обновление: 29.10.2015

Форматировщики медиа-типов отвечают за сериализацию данных для их отправки клиенту. WebApi имеет несколько встроенных форматировщиков. Однако мы не ограничены только этими двумя типами и при желании может создать свой, заточенный под определенные нужды. Все форматировщики содержатся в коллекции HttpConfiguration.Formatters. Эта коллекция представляет объект MediaTypeFormatterCollection и предоставляет прямой доступ к встроенным форматировщикам медиа-типов с помощью следующих свойств:

  • FormUrlEncodedFormatter: возвращает объкт FormUrlEncodedMediaTypeFormatter, который парсит данные формы в процессе привязки модели

  • JsonFormatter: возвращает объект JsonMediaTypeFormatter, который сериализует данные в формат JSON

  • XmlFormatter: возвращает объект XmlMediaTypeFormatter, который сериализует данные в формат xml

Пространство имен System.Net.Http.Formatting также включает еще один класс форматировщика - "BsonMediaTypeFormatter", который управляет сериализацией в формат BSON. Однако в данный момент реализация поддержки спецификации BSON слабо реализована в клиентах. Поэтому по умолчанию данный форматировщик отключен.

Создадим свой класс форматировщика ответа. Для этого определим в проекте для Web Api специальную папку Util, в которую добавим следующий класс BookFormatter:

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using WebApiApp.Models;

namespace WebApiApp.Util
{
    class BookFormatter : MediaTypeFormatter
    {
        public BookFormatter()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/x-books"));
        }
        public override bool CanReadType(Type type)
        {
            return type == typeof(Book) || type == typeof(IEnumerable<Book>);
        }
        public override bool CanWriteType(Type type)
        {
            return type == typeof(Book) || type == typeof(IEnumerable<Book>);
        }
        public override async Task WriteToStreamAsync(Type type, object value,
                Stream writeStream, HttpContent content, TransportContext transportContext)
        {
            List<string> booksString = new List<string>();
            IEnumerable<Book> books = value is Book ? new Book[]  { (Book)value } : (IEnumerable<Book>)value;
            foreach (Book b in books)
            {
                booksString.Add(string.Format("{0},{1},{2}", b.Id, b.Name, b.Year));
            }
            StreamWriter writer = new StreamWriter(writeStream);
            await writer.WriteAsync(string.Join(",", booksString));
            writer.Flush();
        }
    }
}

Для создания форматировщика нам надо унаследовать класс от MediaTypeFormatter и переопределить ряд его методов.

В конструкторе форматировщика добавляется новый медиа-тип:

SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/x-books"));

Форматировщик поддерживает коллекцию SupportedMediaTypes, которая определяет набор mime-типов - объектов MediaTypeHeaderValue, которые используются в заголовках при взаимодействии клиента и сервера. MIME-тип задается по принципу type/subtype. Для личных mime-типов перед подтипом указывается префикс x.

Также переопределяем методы CanReadType и CanWriteType. Метод CanReadType участвует в привязке моделей, а метод CanWriteType позволяет указать, может ли данный форматировщик сериализовать объект данного типа. Форматировщик будет отвечать за работу с типом Book, поэтому он будет обрабатывать те данные, которые представляют либо единичный объект Book, либо коллекцию этих объектов.

Сама сериализация данных производится в асинхронном методе WriteToStreamAsync, который принимает пять параметров:

  • type: тип сериализуемого объекта

  • value: сам объект для сериализации

  • writeStream: поток, в который будет сериализоваться ответ

  • content: объект, предоставляющий доступ к заголовкам ответа

  • transportContext: объект, предоставляющий информацию об используемом сетевом транспорте

Суть метода WriteToStreamAsync - составление из значений отдельных свойств модели строки, в которой все эти значения разделены запятыми. Данный способ сериализации очень примитивен и приводится только в целях демонстрации.

В конце список строк записывается в поток методом WriteAsync(), при этом нам не надо закрывать поток.

После создания форматировщика его надо зарегистрировать в файле WebApiConfig.cs:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
		
		// добавление форматировщика
        config.Formatters.Add(new BookFormatter());
    }
}

Добавление происходит в коллекцию форматировщиков Formatters, которая определена у объекта конфигурации HttpConfiguration.

На уровне контроллера нам менять ничего не надо, методы, как и раньше, отдают клиенту объекты:

public IEnumerable<Book> Get()
{
    IEnumerable<Book> books = new List<Book> 
    { 
        new Book {Id=1, Name="Война и мир", Year=1863},
        new Book {Id=2, Name="Отцы и дети", Year=1862},
        new Book {Id=3, Name="Евгений Онегин", Year=1831},
    };
    return books;
}

И теперь надо настроить получение данных в представлении:

<div id="tableBlock"></div>

@section scripts
{
    <script type="text/javascript">
    $(document).ready(function () {

        GetAllBooks();

    });
    // Получение всех книг по ajax-запросу
    function GetAllBooks() {

        $.ajax({
            url: '/api/values',
            type: 'GET',
            dataType: "text",
            accepts: {
                text: "application/x-books"
            },
            success: function (data) {

                WriteResponse(data);
            }
        });
    };
 // вывод полученных данных на экран
    function WriteResponse(data) {
        var strResult = "<table><th>ID</th><th>Название</th><th>Год Издания</th>";
        var arr = data.split(",");
        for (var i = 0; i < arr.length; i += 3) {
            strResult += "<tr><td>" + arr[i] + "</td><td> " + arr[i+1] + "</td><td>"
                + arr[i+2] + "</td></tr>";
        }
        strResult += "</table>";
        console.log(strResult);
        $("#tableBlock").html(strResult);
    }
   
    </script>
}

Ключевым моментом здесь является установка типа данных и заголовка Accept с принимаемым MIME-типом:

dataType: "text",
accepts: {
        text: "application/x-books"
}

Настройка dataType указывает, что данные ответа надо обрабатывать как текст, а параметр accepts позволит обмениваться с сервером данными в указанном MIME-типе.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850