Cook Computing

Ping WebLogs.com using XML-RPC.NET

February 24, 2003 Written by Charles Cook

I came across some sample XML-RPC.NET code by Scott Watermasysk today. The article describes how to ping Weblogs.com. As I read it I thought it might be worth re-implementing the sample using an interface because I believe this is now the best way of using XML-RPC.NET on the client-side. Also it would be opportunity to illustrate a few more features of the library.

The API for ping WebLogs.com is described here. There is a single method called ping which takes two parameters, the name of the weblog and its url. The method returns a struct containing a boolean as to whether an error occurred and a string containing an error message if an error did occur. So the first steps are to define a struct to represent the return value and an interface to represent the XML-RPC method:


using System;
using CookComputing.XmlRpc;
 
[XmlRpcMissingMapping(MappingAction.Ignore)]
struct PingResult
{
  public XmlRpcBoolean flerror;
  public string message;
}
  
[XmlRpcUrl("http://rpc.weblogs.com/RPC2")]
interface IWebLogs
{
  [XmlRpcMethod("weblogUpdates.ping")]
  PingResult Ping(string weblog, string url);  
}

This code encapsulates the interface to the XML-RPC endpoint. Once defined it can be re-used elsewhere if required. The XmlRpcMissingMapping attribute is used to specify that all the members of the struct are optional (this being XML-RPC you can never be quite sure what a server is going to return). Also, flerror is specified as type XmlRpcBoolean: if we had used bool we would not know whether the server had actually returned this member in the struct. The member would contain a default value of false if it had not been returned. By using XmlRpcBoolean we can check whether the server returned the member by testing whether the member is null, because it can either be true or false if a value was returned or a null reference if it was not.

Scott's example wraps the WebLogs.com call with some extra functionality but before implementing that its worth demonstrating how a proxy can be generated from the interface and used to make calls. Although the proxy code could be written by hand, in most cases it is easier and less error-prone to generate the proxy code programmatically using the XmlRpcProxyGen class in a single line of code as the following example illustrates:


IWebLogs proxy = (IWebLogs)XmlRpcProxyGen.Create(typeof(IWeblogs));
PingResult result = proxy.Ping("My Fictitious Blog", "http://www.fictitiousblog.com");
Console.WriteLine("{0} {1}", result.flerror, result.message);

Returning to Scott's sample, his class which wraps the XML-RPC call can be implemented as:


public class WeblogsNotification
{
  private string errormessage = "No Error";
  public string ErrorMessage
  {
    get{return errormessage;}
  }
 
  public bool Ping(string name, string url)
  {
    bool result = false;
    try
    {   
      IWebLogs proxy = (IWebLogs)XmlRpcProxyGen.Create(typeof(IWebLogs));
      PingResult response = proxy.Ping(
        "My Fictitious Blog", 
        "http://www.fictitiousblog.com");
      if (response.flerror == null || response.flerror == false)
        result = true;
      else
      {
        if (response.message != null)
          errormessage = response.message;
        else    
          errormessage = "Unknown Error";
      }    
    }
    catch (XmlRpcFaultException fex)
    {
      errormessage = fex.FaultString;
    }
    catch (Exception ex)
    {
      errormessage = ex.Message;
    }
    return result;
  }
}

There are a couple of points worth mentioning here. First, because the members of the return struct were specified as optional (using the XmlRpcMissingMapping attribute on the definition of the struct) and are both nullable types, we can test them for being null to determine whether they were actually returned by the server in the struct. Second, we handle the case where the server returned an XML-RPC Fault response by catching XmlRpcFaultException separately.

Finally an instance of the class can be used to make the ping:


class Class1
{
  static void Main(string[] args)
  {
    WeblogsNotification wn = new WeblogsNotification();
    if(wn.Ping("My Fictitious Blog", "http://www.fictitiousblog.com"))
    {
      Console.WriteLine( "Weblogs was notified and accepted");
    }
    else
    {
      Console.WriteLine( wn.ErrorMessage);
    }
  }
}

A couple of final points: for an XML-RPC endpoint such as WebLogs.com there isn't a big advantage to encapsulating its methods in an interface but you'll find that if you need to make calls to endpoints with multiple methods, with possibly more complicated parameters and return values, using interfaces does result in more understandable and reusable code.; also, the use of the XmlRpcProxyGen class does avoid errors which can slip in when proxies are hand-coded.