Search code examples
pythonxmlsoaprpcspyne

Understanding RPC decorators: complex return arguments with SOAP and XML


I am brand new to creating SOAP web services and have a question about RPC decorators.

Anyway, my intention is for my web service to have a somewhat complex type of return (I believe it is its Polymorphic). The input protocol is SOAP and the output protocol is XML.

I would like to return a dictionary that contains an array, each of these with values that could either be floats or strings (see line 6 in the code below). I understand how this would look as an XML element tree -- however, I dont know how to denote this in the _returns parameter of the @rpc decorator. To make matters slightly more complicated, if the client sends invalid credentials, I would simply like to return the string "Invalid credentials". Or perhaps it would be easier to return an empty list...

Some advice on this issue is greatly appreciated!

class CoreService(ServiceBase):
    @rpc(Unicode, Unicode, Integer, _returns=Unicode) #@rpc arguments corespond to the retrieve_score() arguments below
    def retreive_score(ctx, username, password, uid):
        if validate_creds(username,password):
            return {"score:" 0.6, features=[{"gender": "male"}, {"height": 160], ... ]
        else:
            return "Invalid credentials"


application = Application([CoreService], 'spyne.iefp.soap',
                          in_protocol=Soap11(validator='lxml'),
                          out_protocol=XmlDocument(polymorphic=True))

Solution

  • SOAP has one return type. Idiomatic way is to raise exceptions on errors, not returning a rogue response (ie response that is not compatible with the designated return type)

    If you want to return arbitrary data, you can try _return=AnyDict or even better, _return=AnyXml which will let you return anything you want.

    See this function to understand how arbitrary dicts are transformed to XML documents.

    https://github.com/arskom/spyne/blob/b8925ee5dc407eb9e2a8d97047f14b10425ee01d/spyne/util/etreeconv.py#L60

    This function implements one of the possible ways of mapping dictionaries to XML data. It's a lossy process so it may or may not suit your needs.

    If it doesn't work for you, you will have to use the AnyXml type and directly use lxml to construct your response. I suggest the lxml.builder api. See here for more info: https://lxml.de/api/lxml.builder.ElementMaker-class.html