Denis Gladkikh
Russian   |  English

Встраиваем MetaWeblog API на свой сайт

Пару недель назад мне попалась на глаза небольшая статья Feels like a new day – implementing the MetaWeblog API. В тот же момент у меня и родилась мысль, что нужно сделать поддержку MetaWeblog API, а следовательно и Windows Live Writer, у себя на сайте. В прошедшие выходные нашел немного свободного времени и начал исследование ссылок, которые были в статье. Обе ссылки, в которых были примеры реализации, основывались на XML-RPC.NET, мне же хотелось все реализовать на WCF сервисах. В пару кликов нашел статью XML-RPC with WCF (Updated), в которой был небольшой пример реализации на WCF сервисах и реализована библиотека, в которой был XML-RPC Behavior с самодельным сериализатором, поддерживающим XML-RPC. В общем, материал был найден и я приступил к реализации.

Как это часто бывает: библиотека, что я нашел была хорошая, но не идеальная, пример работал, но не на 100%. Начал разбираться по пунктам. Ознакомился так же со спецификацией на MetaWeblog API, который предоставляет Windows Live Writer: MetaWeblog API Reference. Сразу вспомнились основные принципы разработки SOA, если один раз уже выложил плохую реализацию (или какую-нибудь), то мучайся и поддерживай ее до конца, так же и тут встречаются такие вот параметры:

syntax_sample

Дальше я начал описывать все структуры, которые используются в этом API, только сделал я их классами (не понял, почему во всех примерах так все норовят использовать именно структуры). Так же я не стал уподобляется примерам и сделал хранение всех параметров через свойства с нормальными именами:

[DataContract]
public class Post
{
    [DataMember(Name = "dateCreated")]
    public DateTime DateCreated { get; set; }
 
    [DataMember(Name = "date_created_gmt")]
    public DateTime DateCreatedGmt { get; set; }
 
    // ....
}

Кстати, обратите внимание на второе свойство DateCreatedGmt, его в примерах реализаций нет, его я обнаружил через Fiddler:

fiddler2

Нашел, я его, кстати, не случайно, а потому что библиотека, которую я взял падала при попытке распарсить запрос. Она просто не была заточена на то, что в запросе могут быть параметры, которые не ожидаются. Так же у меня она падала и после добавления этого параметра, так как она просто не понимала, что нужно делать с попадающимся текстом “\n   ”, тут мне все таки пришлось добавить небольшой фикс в класс XmlRpcDataContractSerializationHelper (Ln 34):

while (reader.NodeType != XmlNodeType.EndElement)
{
    if (reader.NodeType == XmlNodeType.Text)
    {
        reader.ReadString();
        continue;
    }
 
    //....
}

Но тут проблемы не закончились, обратите внимание на формат передаваемой даты (и это iso8601!), конечно же сериализатор не смог ее распарсить, потому пришлось дописать еще немного кода в том же классе:

public class XmlRpcDataContractSerializationHelper
{
    private const string DefaultDateTimeFormat = "yyyyMMddTHH:mm:ss";
    
    //...
    
    public static object Deserialize(XmlDictionaryReader reader, Type targetType)
    {
        //...            
            // Ln 135
            case XmlRpcProtocol.DateTime:
                string dateTime = reader.ReadElementContentAsString();
                returnValue = Parser.ToDateTime(dateTime, DefaultDateTimeFormat);
                break;
        //...
    }
 
    public static void Serialize(XmlDictionaryWriter writer, object value)
    {
        // ...
            // Ln 260
            else if (valueType == typeof (DateTime))
            {
                writer.WriteStartElement(XmlRpcProtocol.DateTime);
                writer.WriteValue(((DateTime)value).ToString(DefaultDateTimeFormat));
                writer.WriteEndElement();
            }
        // ...
    }
}

Правил я именно для случая с Windows Live Writer, если же хочется использовать эту библиотеку не только там, то лучше бы формат даты вынести в настраиваемые параметры. Используемый метод Parser.ToDateTime очень прост:

public static DateTime ToDateTime(object obj, string format)
{
    IFormatProvider culture = new CultureInfo("en-US");
    return DateTime.ParseExact(obj.ToString(), format, culture);
}

В плане реализации самого сервиса, то там все прозрачно, нужно лишь взять один из примеров и реализовать каждый отдельный метод. Мне очень повезло, что MetaWeblog API предоставляет возможность возвращаться ссылки на несколько блогов для пользователя, потому у хорошо вписалась концепция, что на моем сайте есть, вроде как, два блога: русский и английский. Эти данные возвращаются в методе сервиса blogger.getUsersBlogs. Убедитесь только, что правильно указаны Url на блоги, так как в моем случае я долго не мог понять, почему у меня Windows Live Writer не мог загрузить тему с сайта, оказалось все просто, я указал ссылку http://outcoldman.ru, хотя должен был для каждого языка указать четкую ссылку на страницу, где записи будут видны, для русского языка – это, например, http://outcoldman.ru/ru/blog/index.

В примере к статье  XML-RPC with WCF (Updated) сервис выставляется наружу при помощи настройки в runtime, мне же нужно было правильно все настроить через web.config:

<system.serviceModel>
    <bindings>
        <webHttpBinding>
            <binding name="Custom" openTimeout="01:00:00" sendTimeout="01:00:00" receiveTimeout="01:00:00" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
                <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647"/>
            </binding>
        </webHttpBinding>
    </bindings>
    
    <extensions>
        <behaviorExtensions >
            <add name="xmlRpc" type="Microsoft.Samples.XmlRpc.XmlRpcEndpointBehaviorSection, Microsoft.Samples.XmlRpc"  />
        </behaviorExtensions>
    </extensions>
    <services>
        <service name="PersonalWeb.Metablog.MetaWeblog"  >
            <endpoint binding="webHttpBinding" bindingConfiguration="Custom" contract="PersonalWeb.Metablog.IMetaWeblog" 
                      behaviorConfiguration="XmlRpcEndpoint"  />
        </service>
    </services>
    <behaviors>
        <endpointBehaviors>
            <behavior name="XmlRpcEndpoint"  >
                <xmlRpc />
            </behavior>
        </endpointBehaviors>
    </behaviors>
 
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
 
</system.serviceModel>

Конфигурацию для webHttpBinding пришлось делать после того, как осознал что картинки из Writer не передаются на сервер, тогда увеличил там все параметры сразу же, чтобы потом не натыкаться на другие проблемы. Последняя настройка serviceHostingEnvironment связана с тем, что уже после деплоя всего на рабочий сайт получил ошибку:

This collection already contains an address with scheme http.  There can be at most one address per scheme in this collection. If your service is being hosted in IIS you can fix the problem by setting 'system.serviceModel/serviceHostingEnvironment/multipleSiteBindingsEnabled' to true or specifying 'system.serviceModel/serviceHostingEnvironment/baseAddressPrefixFilters'.
Parameter name: item

Разбираться с ней особо не стал, просто погуглил и нашел самый простой вариант решения, который вы видели выше.

Ну и не забываем сделать файлик на сайте MetaWeblogService.svc:

<%@ ServiceHost Language="C#" Debug="true" Service="PersonalWeb.Metablog.MetaWeblog" %>

Перед тем как деплоить, решал еще несколько проблем. Первая – это не было возможности добавлять свои теги из Writer, опять нагуглил решение – вместо MetaWeblog API в качестве сервиса при настройке блога в Writer нужно указать Community Server и ту же ссылку на свою реализацию MetaWeblog API сервиса. Безумно простое решение, и самое главное оно работает. Вторая проблема – это нет поддержки “Разделителя записи”, чтобы была Abstraction и Body записи (так сказать, что под cut’ом). Эту проблему решил при помощи “<hr />”, ее то вставить в блог можно, а на сервере уже нахожу это место и разрываю спокойно запись (вряд ли мне когда-нибудь реально понадобиться “<hr />”). Еще была странная проблема с датами, возвращаю все в “правильном” формате, который ожидает Writer для существующих статей, и даты даже отображаются в списке предыдущих статей, но никак не хочет их Writer подставлять для редактируемой статьи. Решил просто забить на даты, и выставлять их на сервере при создании статьи. И последняя, самая неприятная для меня, проблема была со стилями сайта, мой сайт раньше не поддерживал IE6/7, а вот Writer рендерит вроде все на IE7 движке, из-за чего тема нормально в нем не отображалась, пришлось потратить небольшое количество времени для переверстки сайта. Вроде получилось, даже убрал специальный хак для Opera. Да, если у вас разъедется разметка – то просто прорефрешьте страницу. Подмигивающая рожица

Вроде, это все проблемы, с которыми я столкнулся при реализации, в итоге около 6 часов работы, больше, наверное, даже поиски всяких решений, ну и в итоге облегчил себе на будущее жизнь.

P.S. Если вы прочли эту статью, значит у меня действительно все работает. Улыбка


Вас также может заинтересовать

rss twitter

Комментарии (22)

Calabonga ( ) #
avatar
Спасибо за статейку. Самое время! Как раз переделываю свой сайт и буду на нем делать свой движок блога. Как раз думал как его через MetaWeblog наполнять.

Еще раз спасибо!
Denis Gladkikh ( ) #
avatar
Мне вот тоже посчасливилось во время увидеть статейку, до этого даже и мыслей не было реализовывать подобное, а оказалось все даже очень просто.
Будут вопросы - обращайтесь.
Sergey Zwezdin ( ) #
avatar
А картинки у тебя тоже заливаются на сайт через MWB API?
Сергей Попов ( ) #
avatar
Публикуй на хабре, поддержу ;-)
Sergey Zwezdin ( ) #
avatar
Хабра-маньяк :-)
Так-то уже там: http://habrahabr.ru/blogs/net/102967/
Denis Gladkikh ( ) #
avatar
Sergey Zwezdin, все так, через метод metaWeblog.newMediaObject, странно что его нет на MSDN. Как я и писал единственная проблема с ними была - это увеличить максимальный объем передаваемых данных.

Сергей Попов, ага, на хабре уже по той ссылке :)
KLUBS ( ) #
avatar
Отлично! Узнал еще о HTTP дебаге. Хорошо что все работает
heeepi ( ) #
avatar
Спасибо за статью. Находил реализацию на XML-RPC, но переделывать не стал, ибо было не критично. Надо будет попробовать для своих блогов прикрутить. Юзеры пожалуй только спасибо скажут.
Denis Gladkikh ( ) #
avatar
heeepi, пожалуйста. Я все хочу дописать эту библиотеку, что нашел, сделать хороший пример и выложить на codeplex, но все руки не доходят :)
Полезность для пользователей - это безусловно большая. Вообще, странно, что только у MS есть такой распространенный продукт, как Writer, особых конкурентов не видно, хотя наверняка же есть.
Sergey Popov ( ) #
avatar
Denis Gladkikh
Полезность для пользователей - это безусловно большая. Вообще, странно, что только у MS есть такой распространенный продукт, как Writer, особых конкурентов не видно, хотя наверняка же есть.

Честно говоря.. Я вот во Writer особого смысле не вижу. Tiny MCE обладает той же функциональностью, и доступен прямо со страницы, для него даже минимальных настроек выполнять не требуется.
Denis Gladkikh ( ) #
avatar
Нафиг, нафиг эти корявые RichTextEditor в браузере. Мучался я с ними одно время здорово. Использовал FCKEditor (у него вроде сейчас другое название). Проблемы стандартные, если это грамотный пользователь, то и выхлоп html там будет хороший, если нет, то после копипаста с других сайтов, с Word и т.п. - там такое мясо будет из разных стилей.
Sergey Popov ( ) #
avatar
Denis Gladkikh
Я знаю, что такое FCK ;-) Так IMHO FCK даже близко рядом с TinyMCE не валялся ;-)
Denis Gladkikh ( ) #
avatar
Сергей, ну мне сложно судить. Я когда выбирал года 4 назад, что использовать FCk или TinyMCE, то первый наоборот был более развит. Точно не помню, почему я сделал этот выбор. В любом случае часто бывает с ним проблем можно набрать.
heeepi ( ) #
avatar
Тоже против писать посты в RichTextEditor в браузере. Это все таки браузер и от кривой реализации сайта никто не застрахован (если например слетела сессия сайта, то вполне можешь потерять ценную заметку). Так что Writer с этой точки зрения надежней. В последнее время правда и от него отказался, начал использовать постинг через почту. Удобно.
Calabonga ( ) #
avatar
Доброго времени суток, уважаемый!

Выдались свободные выходные и вот решил "прикрутить" к своему блогу MetaWeblog API. Вроде получилось, но не всё. Остался без ответа вопрос: как добавлять свои теги из Writer? Наведите на правильный путь, пожалуйста.
Denis Gladkikh ( ) #
avatar
Calabonga, а что именно не получается? Есть же в описании метод getCategories, так же есть в самом объекте Post массив категорий - это и есть теги.

Просто, чтобы они отображались в Writer, нужно регистрировать блог не как metaweblogAPI, а как Community Server.
Calabonga ( ) #
avatar
Мне удалось:

- посты добавляются, редактируются, удаляются;

- есть выбор категории;

- медиа-объекты тоже сохраняются.

Не удалось:

- загрузка и выбор тегов(tags).

Я попробывал подключится к своему WordPress - в WLW появляется выбор тегов, а вот мой блог такую возможность в WLW не дает. Очевидно, что надо реализовать еще какой-то интерфей, чтобы WLW "понял" что есть еще и теги (ключевые слова). Вот про эти-то теги (интерфейс для реализации) я и пытаюсь найти информацию.
Calabonga ( ) #
avatar
Сам нашел как настроить Windows live writer. Всё описано подроно http://msdn.microsoft.com/en-us/library/bb463260(v=MSDN.10).aspx

Так что я теперь всё реализовал, и даже больше чем просто MetaWeblog API... :)

Спасибо!
Denis Gladkikh ( ) #
avatar
Calabonga, спасибо, полезная ссылка.
Calabonga ( ) #
avatar
Ну, тогда вот еще одна полезная ссылка:

http://www.web-junior.net/xml-rpc-posting-v-wordpress/

Полезность заключается в описание некоторых полей в сущностях.
Vyacheslav Bukharin ( ) #
avatar
Спасибо, отличная статья, хотя немного пришлось попарится с темами. У меня возник такой конфуз:

Когда wlw пытается определить тему - он создает тестовую новость, обращается к странице, "находит" ее там и скачивает все стили + изображения. Но проблема в том, что он добавляет тестовую новость с параметром publish = false, и логично что я эту новость не отображаю на странице, которую он запрашивает, следовательно тему он не обновляет...

что я не так делаю?
Denis Gladkikh ( ) #
avatar
Vyacheslav Bukharin, честно, не помню, может быть и так. Тогда просто временно нужно показывать :)
Добавить комментарий

Если вы хотите получать уведомления о новых комментариях к данному топику, укажите, пожалуйста, email и отметьте соответствующий пункт в форме. Если вы хотите добавить код в тексте комментария, то заключите его внутри тега [code]...[/code], более того можно уточнить язык, на котором написан данный код при помощи [code cs]...[/code], где вместо cs могут быть cs, html, xml, java, js, php, sql, cpp, css.

 

busy