views:

252

answers:

3

I think I hit a problem when using C# client to consume Google App Engine Webservice. The Google App Engine code I use is here. This is how the python script on server would look like:

from google.appengine.ext import webapp                                        
from google.appengine.ext.webapp.util import run_wsgi_app                      
import logging                                                                 

from StringIO import StringIO                                                  
import traceback
import xmlrpclib
from xmlrpcserver import XmlRpcServer

class Application:
    def __init__(self):
        pass                    

    def getName(self,meta):                                                    
        return 'example'

class XMLRpcHandler(webapp.RequestHandler):                                    
    rpcserver = None

    def __init__(self):         
        self.rpcserver = XmlRpcServer()                                        
        app = Application()                                                    
        self.rpcserver.register_class('app',app)                               

    def post(self):
        request = StringIO(self.request.body)
        request.seek(0)                                                        
        response = StringIO()                                                  
        try:
            self.rpcserver.execute(request, response, None)                    
        except Exception, e:                                                   
            logging.error('Error executing: '+str(e))                          
            for line in traceback.format_exc().split('\n'):                    
                logging.error(line)
        finally:
            response.seek(0)  

        rstr = response.read()                                                 
        self.response.headers['Content-type'] = 'text/xml'                     
        self.response.headers['Content-length'] = "%d"%len(rstr)               
        self.response.out.write(rstr)

application = webapp.WSGIApplication(                                          
                                     [('/xmlrpc/', XMLRpcHandler)],
                                     debug=True)                               
def main():
  run_wsgi_app(application)                                                    

if __name__ == "__main__":
    main()

The client side ( in Python) is this:

import xmlrpclib
s = xmlrpclib.Server('http://localhost:8080/xmlrpc/')
print s.app.getName()

I have no problem in using Python client to retrieve values from Google App Engine, but I do have difficulties in using a C# client to retrieve the values. The error I got was 404 method not found when I am trying to GetResponse from the web request.

This is my code

        var request = (HttpWebRequest)WebRequest.Create("http://localhost:8080/xmlrpc/app");

        request.Method = "GET";
        request.ContentLength = 0;
        request.ContentType = "text/xml";
        using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) //404 method not found error here.
        {

        }  

Edit: For end points, I've tried:

  1. http://localhost:8080/xmlrpc
  2. http://localhost:8080/xmlrpc/
  3. http://localhost:8080/xmlrpc/app
  4. http://localhost:8080/xmlrpc/app/

But none works

I think it must be that the url is wrong, but I don't know how to get it right. Any idea?

A: 

The regular expression in your App Engine app for the endpoint is exactly '/xmlrpc/', which is what you use in the Python test, but in your C# client you're using '/xmlrpc/app', which isn't mapped to anything.

Nick Johnson
@Nick, I've tried all the combination of end points I can come up with ( see updated question), and yet still I can't work.
Ngu Soon Hui
So what do the logs of your App Engine app show for incoming requests? You'll know what endpoint it's actually using from that.
Nick Johnson
+1  A: 

I guess what happens is that your request sent using HttpWebRequest is missing actual content; which should be your rpc method information in xml format. Please check if code below would work for you; it should send request to http://localhost:8080/xmlrpc/ and dump resulting xml into console.

// send request
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:8080/xmlrpc/");
request.Method = "POST";
request.ContentType = "text/xml; encoding=utf-8";

string content = "<?xml version='1.0'?><methodCall><methodName>app.getName</methodName><params></params></methodCall>"; 
byte[] contentBytes = System.Text.UTF8Encoding.UTF8.GetBytes(content);                      
request.ContentLength = contentBytes.Length;
using (Stream stream = request.GetRequestStream())
{
    stream.Write(contentBytes, 0, contentBytes.Length); 
}

// get response
WebResponse response = request.GetResponse();
XmlDocument xmlDoc = new XmlDocument();
using (Stream responseStream = response.GetResponseStream())
{
    xmlDoc.Load(responseStream);
    Console.WriteLine(xmlDoc.OuterXml);
}       

hope this helps, regards

serge_gubenko
Your answer works! Thanks. May I know where can I find more reference about this?
Ngu Soon Hui
I guess reading up on how they work might help. Also you can always use sniffer tools like Wireshark to see what's actually getting sent and received through http\tcp when you're communicating to the server using higher level apis
serge_gubenko
+1  A: 

In addition to the excellent answer posted here, one can also use xml-rpc.net to do the job.

Here's the code on the server side, assuming that getName now takes a string parameter:

def getName(self,meta, keyInput):                                                    
    return keyInput

And this would be the C# Client code, by making use of xml-rpc.net:

[XmlRpcUrl("http://localhost:8080/xmlrpc")]
public interface IMath : IXmlRpcProxy
{
    [XmlRpcMethod("app.getName")]
    string GetName(string number);

}

public string GetName(string keyInput)
{
        var  mathProxy = XmlRpcProxyGen.Create<IMath>();
        mathProxy.Url = "http://localhost:8080/xmlrpc/";
        return mathProxy.GetName(keyInput);

}

Hope this helps everyone who's struggling to make rpc call to GAE from C# client.

Ngu Soon Hui