xmlrpc - путь django

Недавно встал вопрос о реализации пингбека для блога. Как известно серверная часть этого протокола организуется через xml-rpc. И тут всё отлично, в стандартную поставку питона входит простой xml-rpc сервер, которого с головой хватает для организации standalone сервера. Но зачем ещё один сервер если у нас есть django?! Да и потом, не охота мучиться с интеграцией одного с другим. Поэтому идея простая: запросно/ответную часть отдать на откуп джанги, от нас же требуется только корректное следование протоколу по части xml.

Как это обычно бывает, что "всё уже украдено до нас". Есть реализация встраемого в джанго xml-rpc сервера. Не плохая, очень простая, потому что xml'ная часть там реализуется через тот же стандартный SimpleXMLRPCServer, т.к. там в недрах есть всё необходимое, главное знать как использовать (люблю OpenSource:)). В реализации от Пираньи пингбека тоже используется похожий метод, но чуть-чуть более гибкий.

Ну так вот, вдохновившись от увиденного и позаимствовав немного идей и кода, решил сделать своё. Получилось как мне кажется достаточно удобно.

Итак, первое чтобы решено сделать, это дать возможность достаточно просто делать гейты для запросов где угодно, т.е. чтобы разные приложения могли иметь свои xml-rpc "серверы" под свои нужды.

Не буду много рассказывать, лучше покажу код. Универсальный гейт для запросов, к которому цепляются методы:

from SimpleXMLRPCServer import SimpleXMLRPCDispatcher

from django.http import HttpResponse

class ServerGateway( object ):
    def __init__( self, prefix ):
        self.prefix = prefix
        try:
            # Python 2.4
            self.dispatcher = SimpleXMLRPCDispatcher()
        except TypeError:
            # Python 2.5
            self.dispatcher = SimpleXMLRPCDispatcher(allow_none=False, encoding=None)

    def add_function(self, name, func ):
        self.dispatcher.register_function( func, ".".join( [self.prefix, name] ) )

    def connect( self, func ):
        self.add_function( func.__name__, func )
        return func

    def __call__(self, request, *args, **kwargs ):
        if kwargs:
            raise RuntimeError, "Xmlrpc server gateway cannot handle key variable argumets"

        def custom_dispatch( method, params ):
            return self.dispatcher._dispatch(method, params + tuple( args ) )

        response = HttpResponse()
        if len(request.POST):
            response.write( self.dispatcher._marshaled_dispatch( request.raw_post_data, custom_dispatch ) )
        else:
            methods = self.dispatcher.system_listMethods()

            response['Content-Type'] = 'text/plain'
            for method in methods:
                # __doc__
                help = self.dispatcher.system_methodHelp(method)
                response.write("%s:\n    %s\n\n" % (method, help))

        response['Content-Length'] = str(len(response.content))
        return response

Метод прицепить можно так:

gateway = ServerGateway( "pingback" )

@gateway.connect
def ping( source_uri, target_uri, model ):
    #...

Имя функции автоматически становится именем метода(тут просто для улучшения чтобы можно было задать другое имя, но это я пока решил не делать). Поскольку объект gateway callable, то его можно использовать в качестве view и прописывать в urls.py. Через него будут проходить запросы и он дальше будет тянуть нужный метод.

Но есть маленькая особенность, он может передавать дополнительные параметры в методы. Эти параметры(позиционные) он цепляет из урла, обычным для джанги способом и потом передает в метод. Достаточно удобно и позволяет добиться дополнительной гибкости, которую я далее продемонстрирую.

Исходя из первоначальной задачи, и подумав ещё, пришел к выводу, что пингбек может быть не только для блога, но и для любого приложения в котором он будет к месту. Поэтому привязывать его к определенно модели не стоит. Для того чтобы облегчить работу пингбек реализации, название модели будет частью юрла к которому прицеплен гейт. Далее имя модели в виде параметра будет предаваться в метод ping. А там уж внутри по этому имени будет легко понять какую модель "пингуем" и т.д. Поскольку имея xml-rpc гейта указывается в специальном мете-теге на странице, то проблем с направление пинга в нужный обработчик нет.

Для указания урла можно использовать стандартный тег {%url%}. Но для пингбека я написал свой вспомогательный:

@register.simple_tag
def pingback_gateway( model ):
    url = reverse( "generics.pingback.views.gateway", args = ( model, ) )
    return '<link rel="pingback" href="%s" />' % urljoin( "http://" + Site.objects.get_current().domain, url )

Подведем итог. Получилась удобная(на мой взгляд конечно) реализация встраивания xml-rpc сервера в django с богатыми возможностями интегрирования в различные приложения. И одной приятной особоеностью - передачей доп параметров.

А как вам, понравилось? Хотелось бы услышать критику и другие идеи на счет xml-rpc.

Комментарии 2

  1. Александр Кошелев написал:

    Красиво! Намного лучше моего велосипеда :)

    Спасибо:) Но и тут есть что править

    Оставлен 30 Декабрь 2007 в 02:30
  2. Vitaliy написал:

    А как вам, понравилось?

    Красиво! Намного лучше моего велосипеда :)

    Оставлен 31 Декабрь 2007 в 05:22

Пингбеки 1

  1. От Web-service на Django and Python 27 Март 2010 в 15:00

    n на котором собственно http://bitbucket.org/ и работает 4. XML-services Ну здесь одной джангой можно обойтись - прочто в шаблонах писать не html, а xml Что касается просто питона, то поддержка xml-rpc включена в стандартную библиотеку. Также есть поддержка некоторых типов сервисов в других фреймворках на питоне, например pyl