Tuesday, February 21, 2012

Consuming XML-RPC web services with C# - Part 3 - let others do the work

At Part 1 and Part 2 I have shown the raw way and the object oriented approach to consume xml-rpc web services.

This time, we take a look at a helper to simplify the process of developing and consuming XML-RPC web services.


An easy to learn and useable library is xml-rpc.net.

xml-rpc.net enables you to focus on the main target and doesn't waste your time with developing complicated call scenarios.

Some benefits of xml-rpc are:
  • interface based definition of server and client
  • type safe code generation at runtime
  • client support for asynchronous calls
  • various encodings and identitation
  • mapping of XML-RPC method names CLS-compliant method, field and class names

So we are going straight forward and assuming the same web service as in Part 2.

The base xml structure still looks like this:

  Service.method
  
                                     memerName             memverValue                                
To call a method on the server we have to define an interface as proxy derived from IXmlRpcProxy. Assuming we are requesting the version of the server our interfaces has the following structure:
[XmlRpcUrl("https://bugzilla.mozilla.org/xmlrpc.cgi")]
public interface IBugzilla : IXmlRpcProxy
{
  [XmlRpcMethod("Bugzilla.version")]
  VersionResult Version();
}

public class VersionResult
{
  [XmlRpcMember("version")]
  public string Version { get; set; }
}
As you see we also have to define a data structure that holds the result data. Thats all so far. Now we can do a request:
IBugzilla bugzilla = XmlRpcProxyGen.Create();
VersionResult result = bugzilla.Version();
Thats all, you're done! xml-rpc.net is handling the serialization, communication and result retrieval for you. Faults, errors that are caused by the server, are thrown as XmlRpcFaultException so that it could be handled explicit by user code. Happy coding! Update 2012-02-21: I've prepared a complete using sample for log in on bugzilla.mozilla.org. The sample consists of a proxy interface for the User service to login and retrieve user information.
[XmlRpcUrl("https://bugzilla.mozilla.org/xmlrpc.cgi")]
public interface IUserService : IXmlRpcProxy
{
   [XmlRpcMethod("User.login")]
   LogInResult LogIn(LogInRequest logInRequest);

   [XmlRpcMethod("User.get")]
   UserInfoResponse Get(UserInfoRequest userInfoRequest);

   [XmlRpcMethod("User.logout")]
   void LogOut();
}
Where LogInRequest is implemented as follows
public class LogInRequest
{
   [XmlRpcMember("login")]
   public string User { get; set; }

   [XmlRpcMember("password")]
   public string Password { get; set; }

   [XmlRpcMember("remember")]
   public bool Remember { get; set; }
}
The other contracts are defined the same way. The proxy is now useable like this:
var userService = XmlRpcProxyGen.Create<IUserService>();

var logIn = userService.LogIn(new LogInRequest
                       {
                          User = ConfigurationManager.AppSettings["user"],
                          Password = ConfigurationManager.AppSettings["password"]
                       });

var userInfo = userService.Get(new UserInfoRequest { Ids = new[] { logIn.Id }});

if (userInfo.UserInfos.Any())
{
   var first = userInfo.UserInfos.First();

   // just do something awesome
}

userService.LogOut();
This was the last part of this series. Make sure you read also Part 1 and Part 2. UPDATE 2013-08-29: Sample codes of this part are now available at github. Codes of all samples could you find here.

10 comments:

  1. Can you please provide an example of how to log into Bugzilla using XmlRpc.Net?

    I'm trying this:

    IBugzilla bugzilla = XmlRpcProxyGen.Create();
    bugzilla.Credentials = new NetworkCredential("myUserId", "myPassword");

    But trying to create a new bug I am getting: "[410] You must log in before using this part of Bugzilla."

    ReplyDelete
  2. You have to use an Proxy for http://www.bugzilla.org/docs/tip/en/html/api/Bugzilla/WebService/User.html to call the login method.

    Store cookies of response and send them with each call of the session.

    ReplyDelete
  3. Can you please give example of using the proxy?

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Hi, thanks a lot for this tutorial.

    Nevertheless I'm facing a conversion issue that seems not solve here and I don't find any solution from the website. I request your help on this point please.

    My problem is with deserialization of hasmap that contains a key,value pair. For example, this XML-RPC command of Bugzilla returns Hashmap with key;value pair.
    For example, the following commands return hasmp with key value pair :
    http://www.bugzilla.org/docs/4.2/en/html/api/Bugzilla/WebService/Bug.html#comments
    http://www.bugzilla.org/docs/4.2/en/html/api/Bugzilla/WebService/Bugzilla.html#extensions


    I tried with the following with no success:

    //[XmlRpcMember("bugs")]
    //public CommentBugStruct Bugs
    //{
    // get { return bugs; }
    // set { bugs = value; }
    //}

    //[XmlRpcMember("bugs")]
    //public Dictionary Bugs
    //{
    // get { return bugs; }
    // set { bugs = value; }
    //}

    [XmlRpcMember("bugs")]
    public KeyValuePair[] Bugs
    {
    get { return bugs; }
    set { bugs = value; }
    }

    ReplyDelete
    Replies
    1. Hi there, take a look @ Query_Bug_Comment in XmlRpc.cs

      Delete
    2. Hi Torsten, thanks a lot for your quick answer. that works

      Delete
  6. Hi Torsten,

    Do you have any idea on how to implement the "include_fields" and "exclude_fields" feature described here? :
    http://www.bugzilla.org/docs/4.2/en/html/api/Bugzilla/WebService.html#Limiting_What_Fields_Are_Returned

    ReplyDelete
  7. I made some test with "bug.get" function with no successful results.

    ReplyDelete