상세 컨텐츠

본문 제목

.Net XML-RPC

.Net General/.Net XML-RPC

by 탑~! 2009. 9. 18. 14:52

본문

Draft version 0.95 6th January 2003. © Charles Cook, 2002, 2003.

Please send feedback, corrections, etc, to xmlrpcfaq@cookcomputing.com.

Contents

1. Introduction

  1.1 What is XML-RPC?
  1.2 What is XML-RPC.NET?
  1.3 Why use XML-RPC instead of SOAP?
  1.4 Where do I obtain XML-RPC.NET from?
  1.5 How do I install XML-RPC.NET?
  1.6 Can I install XML-RPC.NET in the GAC?
  1.7 How do I build with XML-RPC.NET?
  1.8 How does XML-RPC.NET represent XML-RPC requests and responses?
  1.9 How are XML-RPC simple types mapped onto .NET data types?
  1.10 How are XML-RPC structs represented as .NET types?
  1.11 What if the XML-RPC struct member name is not valid in .NET?
  1.12 How are XML-RPC arrays represented as .NET types?
  1.13 Is XML-RPC.NET CLS Compliant?
  1.14 Why are my struct members ignored?

2. Clients

  2.1 How do I implement an XML-RPC client?
  2.2 What if the XML-RPC method name is not valid in my programming language?
  2.3 Why does my client throw exception "Invoke on non-existent proxy method"?
  2.4 Can I specify the server endpoint URL at runtime?
  2.5 How do I set a timeout on a proxy method call?
  2.6 How do I supply authentication credentials?
  2.7 Can I specify custom HTTP headers?
  2.8 Why does the method name have to be hard-coded in each proxy method?
  2.9 How do I make an asynchronous XML-RPC request?
  2.10 How do I poll for the result of an asynchronous call?
  2.11 How do I receive a callback on completion of an asynchronous call?
  2.12 What is the asyncState parameter used for?
  2.13 How do I implement a client in VB.NET?
  2.14 Why doesn't the following code cause an exception?
  2.15 How do I implement a client in Managed C++?
  2.16 How do I implement a client in JScript.NET?
  2.17 Do classes derived from XmlRpcClientProtocol support the Introspection API?
  2.18 Can I define a proxy method to return void?
  2.19 Does XmlRpcClientProtocol.Invoke use params for the array of parameters?
  2.20 How do I specify a proxy server when making an XML-RPC request?

3. Servers

  3.1 What are the different ways in which an XML-RPC server can be implemented?  
  3.2 How do I implement an XML-RPC server in IIS?
  3.3 How do I implement an XML-RPC server using .NET Remoting?
  3.4 How do I implement an XML-RPC server in IIS using .NET Remoting?
  3.5 Do XML-RPC.NET servers implement the XML-RPC Introspection API?
  3.6 What is Automatic Documentation?
  3.7 How do I support authentication in my XML-RPC service?
  3.8 Can I implement a server which supports both XML-RPC and SOAP?
  3.9 Can I run XML RPC.NET services with other web servers?
  3.10 Can I implement Services in other languages?
  3.11 Can I implement Services in other languages?

4. Error Handling

  4.1 How are XML-RPC Fault Responses represented?
  4.2 How are other types of error returned?
  4.3 What happens if the return value of a proxy method is incorrect?

5. Debugging

  5.1 How do I monitor XML-RPC calls across the network?
  5.2 How do I debug an XML-RPC.NET service?

6. Miscellaneous

  6.1 Which XML Encodings Are Supported?
  6.2 Which Code Access Security Permissions are Required?
  6.3 Why is the key file not included in the distribution?
  6.4 How do I verify an XML-RPC.NET assembly is genuine?
  6.5 Can I define an interface from which both the proxy and server classes are derived?

7. Resources

  7.1 XML-RPC Specification
  7.2 Books
  7.3 Websites
  7.4 Mailing Lists
  7.5 Articles and Tutorials

1. Introduction

1.1 What is XML-RPC?

To quote the XML-RPC.Com site:

"It's a spec and a set of implementations that allow software running on disparate operating systems, running in different environments to make procedure calls over the Internet. It's remote procedure calling using HTTP as the transport and XML as the encoding. XML-RPC is designed to be as simple as possible, while allowing complex data structures to be transmitted, processed and returned."

1.2 What is XML-RPC.NET?

XML-RPC.NET is a .NET class library for implementing XML-RPC clients and servers.

1.3 Why use XML-RPC instead of SOAP?

If your clients and servers are all running in the .NET environment there is no point in using XML-RPC: .NET provides excellent support for SOAP and XML-RPC doesn't have any features not provided by SOAP.

If you use .NET clients and want to connect to XML-RPC servers running under any OS then XML-RPC.NET is a good choice.

If you want to implement a server in the .NET environment which is to be connected to by clients running in other environments, say Unix or Java, then XML-RPC may be an appropriate choice. SOAP is supported in many different environments but is considerably more complicated than XML-RPC and presents more chance of interop problems.

1.4 Where do I obtain XML-RPC.NET from?

XML-RPC.NET can be obtained from the XML-RPC.NET home page.

Updates are announced at Cook Computing and the Yahoo XMLRPCNET group.

1.5 How do I install XML-RPC.NET?

XML-RPC.NET is distributed as a single assembly called CookComputing.XmlRpc.dll which contains both client and server classes. When used by another program, installation is simply a matter of ensuring that the assembly is in the same directory as the program using it or referred to using the program's config file if in another directory.

In the case of an XML-RPC web service running in IIS, the CookComputing.XmlRpc.dll assembly is placed in the bin sub-directory of the virtual directory which is being used for the web service.

1.6 Can I install XML-RPC.NET in the GAC?

XML-RPC.NET is built with a strong name and so can be added to the Global Assembly Cache (GAC). To do this use the gacutil utility:

gacutil /i CookComputing.XmlRpc.dll

Alternatively the dll can be dragged and dropped into the winnt\assembly directory using Windows Explorer.

1.7 How do I build with XML-RPC.NET?

When compiling code which uses any of the classes in the XML-RPC.NET assembly, a reference to the assembly must be specified.

In Visual Studio .NET this is is achieved by using the References dialog which is invoked from the References item in the Solution Explorer. If compiling with the csc.exe compiler, the XmlRpc assembly is referenced like this:

csc /r:CookComputing.XmlRpc.dll myprogram.cs 

1.8 How does XML-RPC.NET represent XML-RPC requests and responses?

XML-RPC.NET represents each XML-RPC server endpoint on both client and server sides using custom classes which have a number of public methods, each method representing one of the XML-RPC methods of the endpoint. The methods on the server side class implement the functionality of the endpoint and the methods on the client side class act as proxy methods.

For example, the server side class implementing the sumAndDifference API could be coded like this:

struct SumAndDiffValue 
{
  public int sum; 
  public int difference; 
}


class SumAndDiffService : XmlRpcService
{ 
  [XmlRpcMethod["sample.sumAndDifference"] 
  public SumAndDiffValue SumAndDifference(int x, int y) 
  { 
    SumAndDiffValue ret; 
    ret.sum = x + y; 
    ret.difference = x – y; 
    return ret; 
  } 
} 

The corresponding client side proxy class could look like this:

[XmlRpcUrl("http://www.cookcomputing.com/sumAndDiff.rem")] 
class SumAndDiffProxy : XmlRpcClientProtocol 
{ 
  [XmlRpcMethod("samples.sumAndDifference")] 
  SumAndDiffValue SumAndDifference(int x, int y) 
  { 
    return (SumAndDiffValue)Invoke("SumAndDifference", 
                                   new Object[]{ x, y }); 
  } 
} 

A client would use the proxy class like this:

SumAndDiffProxy proxy = new SumAndDiffProxy(); 
SumAndDiffValue ret = proxy->SumAndDifference(2, 3); 

1.9 How are XML-RPC simple types mapped onto .NET data types?

XML-RPC simple types are mapped to and from the following .NET types:

<i4> or <int> System.Int32 XML-RPC.NET always outputs <i4>
<boolean> System.Boolean  
<string> System.String XML-RPC.NET always outputs the <string> element instead of just a <value> element with text content
<double> System.Double  
<dateTime.iso8601> System.DateTime Note that the format used in XML-RPC -YYYYMMDDTHH:mm:SS - is not one of the formats described in the ISO8601 specification
<base64> System.Byte[]  

1.10 How are XML-RPC structs represented as .NET types?

An XML-RPC struct is a dictionary of name+member pairs and so is much more flexible than a struct in C++ or C#. Depending on the particular XML-RPC method, the members of a struct are not necessarily fixed: a struct might contain a fixed set of members, a subset of a known set of members, or even an arbitrary set of members created at runtime.

For cases where the members of the struct are fixed, the XML-RPC struct can be mapped onto a .NET struct, for example:

<struct>
  <member>
    <name>code</name>
    <value><int>2</int></value>
  </member>
  <member>
    <name>description</name>
    <value><string>File missing.</string></value>
  </member>
</struct>

could map onto this C# struct:

struct errStruct
{ public int code; public string description; }

When mapping from an XML-RPC struct to a .NET struct, XML-RPC.NET ignores any members which are only in the XML-RPC struct, but throws an exception if a member is defined in the .NET struct but is not in the XML-RPC struct.

When mapping from a .NET struct to an XML-RPC struct, XML-RPC.NET simply maps every member of the .NET struct to the XML-RPC struct.

In cases where the usage of the struct in the XML-RPC call is too flexible to be represented by a fixed data type, the XmlRpcStruct type can be used. This is a derivation of the .NET hashtable type and consists of name/member pairs representing the members of the XML-RPC struct. For example:

XmlRpcStruct bounds = new XmlRpcStruct();
bounds.Add("lowerBound", 18);
bounds.Add("upperBound", 139);
...
lowerBound = (int)bounds["lowerBound"];
upperBound = (int)bounds["upperBound"];

1.11 What if the XML-RPC struct member name is not valid in .NET?

In some cases the name of a member in an XML-RPC struct might be invalid in the .NET programming language being used. To handle this the XmlRpcMember attribute is available. This allows an XML-RPC member name to be mapped to and from a different .NET name. For example:

public struct SumAndDiffValue
{ 
  [XmlRpcMember("samples.sum")] 
  public int sum; 
  [XmlRpcMember("samples.difference")] 
  public int difference; 
}

1.12 How are XML-RPC arrays represented as .NET types?

Where possible XML-RPC.NET maps XML-RPC arrays onto arrays of .NET types. Where this is not possible, for example where the members of the XML-RPC array are not of the same type, the mapping is to an instance of System.Object[].

XML-RPC.NET does not support "jagged" arrays - arrays of arrays - because these are not CLS compliant.

1.13 Is XML-RPC.NET CLS Compliant?

XML-RPC.NET is fully Common Language Specification (CLS) compliant. This means that it can be used from any .NET language which is also CLS compliant, for example C#, VB.NET, or Managed Extensions for C++.

1.14 Why are my struct members ignored?

A common mistake with structs is to define them like this in C#:

struct SumAndDiffValue
{ 
  int sum;
  int difference; 
}

or like this in VB.NET:

Public Structure SumAndDiffValue
  sum As Integer 
  difference As Integer 
End Structure

The mistake is that the members are not defined as public. This means the XML-RPC.NET library cannot recognize them without requiring extra security permissions associated with reflection. This might not be available in all circumstances, for example where the XML-RPC.NET assembly is downloaded by a client running in the Internet security zone, and so struct members are required to have public visibility. To take the C# case, the correct version of the struct would be:

struct SumAndDiffValue
{ 
  public int sum;
  public int difference; 
}

2. Clients

2.1 How do I implement an XML-RPC client?

To make calls to an XML-RPC server it is necessary to use a proxy class. There are two ways of implementing this: using the XmlRpcProxyGen class to build a proxy class dynamically, or by manually coding the class.

Using XmlRpcProxyGen

First devise an interface which represents the methods of XML-RPC server endpoint. For example:

using CookComputing.XmlRpc;

struct SumAndDiffValue 
{
  public int sum; 
  public int difference; 
}

[XmlRpcUrl("http://www.cookcomputing.com/sumAndDiff.rem")] 
interface ISumAndDiff
{ 
  [XmlRpcMethod] 
  SumAndDiffValue SumAndDifference(int x, int y);
} 

Second, create an instance of a dynamically created proxy class:

ISumAndDiff proxy = (ISumAndDiff)XmlRpcProxyGen.Create(typeof(ISumAndDiff));

Third, call a method on the interface:

SumAndDiffValue ret = proxy.SumAndDifference(2, 3);

Manually Coding a Proxy Class

If the XmlRpcProxyGen class is not used to create the proxy class, the class must be coded manually.

using CookComputing.XmlRpc;

struct SumAndDiffValue 
{
  public int sum; 
  public int difference; 
}

[XmlRpcUrl("http://www.cookcomputing.com/sumAndDiff.rem")] 
class SumAndDiffProxy : XmlRpcClientProtocol 
{ 
  [XmlRpcMethod] 
  SumAndDiffValue SumAndDifference(int x, int y) 
  { 
    return (SumAndDiffValue)Invoke("SumAndDifference", 
                                   new Object[]{ x, y }); 
  } 
} 

The using statement is for convenience so the XmlRpc types don't have to be qualified by namespace.

The SumAndDiffValue is defined to represent the result of the samples.sumAndDifference XML-RPC call. Each member of the struct represents a member of the XML-RPC struct returned by the call. Note that members must be defined as public.

The XmlRpcUrl attribute is applied to the proxy class to specify the URL of the server endpoint.

The class being defined derives from XmlRpcClientProtocol to inherit the required proxy functionality.

Each method representing an XML-RPC method is marked with the XmlRpcMethod attribute.

The SumAndDifference method is defined to represent the XML-RPC call. The member function Invoke (inherited from XmlRpcClientProtocol) is called, passing the name of the current function and the parameters to the call in an array of type Object. The value returned from Invoke is cast to the required return type for the function and returned.

Note that the first parameter to invoke is always the name of the proxy function, NOT the name of the XML-RPC method - the next question describes situations in which these names are different (the above code assumes the XML-RPC method name is SumAndDifference).

Use the proxy class like this:

SumAndDiffProxy proxy = new SumAndDiffProxy();
SumAndDiffValue ret = proxy.SumAndDifference(2, 3);

2.2 What if the XML-RPC method name is not valid in my programming language?

Sometimes the XML-RPC method name cannot be used as a method name in the proxy class. For example, it is common practice for XML-RPC method names to have the form namespace.methodname, such as samples.SumAndDifference In these cases a different constructor is used for the XmlRpcMethod attribute, taking a string which specifies the XML-RPC method name. For example:

[XmlRpcUrl("http://www.cookcomputing.com/sumAndDiff.rem")] 
class SumAndDiffProxy : XmlRpcClientProtocol 
{ 
  [XmlRpcMethod("samples.sumAndDifference")] 
  SumAndDiffValue SumAndDifference(int x, int y) 
  { 
    return (SumAndDiffValue)Invoke("SumAndDifference", 
new Object[]{ x, y }); } }

2.3 Why does my client throw exception "Invoke on non-existent proxy method"?

When manually implementing a proxy method there are two method names involved: the name of the proxy method and the name of the XML-RPC method. As the previous answer explained, these names are not necessarily the same, and if the first parameter of Invoke is the XML-RPC method name, and not the proxy method name, an exception is thrown with the message: "Invoke on non-existent proxy method". This problem can be avoided by following these rules:

  1. The call to Invoke always passes the name of the proxy method.
  2. If the the name of XML-RPC method is not the same as the name of the proxy method, the XML-RPC method name is specified by passing it to the XmlRpcMethod attribute.

The reason for this is that there are two stages of serialization when making an XML-RPC call. The first is performed by the implementation of the proxy method, converting the call into a method name and an array of type Object which contains the parameters.This representation of the call is then passed to the XmlRpcClientProtocol class using the Invoke method. The second stage is when the method name and parameter array are converted into the XML request.

For the second stage to work effectively it needs to how the parameters should be mapped onto the params element of the XML request and what method name should be specified in the methodCall element. The first point is handled by using reflection on the proxy method, hence the need for the proxy method name, and the second point is handled by using the name specified in the XmlRpcMethod attribute (or the proxy method name if a name is not specified in the attribute).

2.4 Can I specify the server endpoint URL at runtime?

Yes, classes derived from XmlRpcClientProtocol inherit a Url property. This means that the XmlRpcUrl attribute can be omitted from the definition of the proxy class and instead the Url property is then set on an instance of the class:

SumAndDiffProxy proxy = new SumAndDiffProxy(); 
proxy.Url = "http://www.cookcomputing.com/SumAndDiff.rem");
SumAndDiffValue ret = proxy->SumAndDifference(2, 3);

2.5 How do I set a timeout on a proxy method call?

Classes derived from XmlRpcClientProtocol inherit a Timeout property. This takes an integer which specifies the timeout in milliseconds. For example, to set a 5 second timeout:

SumAndDiffProxy proxy = new SumAndDiffProxy(); 
proxy.Timeout = 5000;
SumAndDiffValue ret = proxy->SumAndDifference(2, 3);

2.6 How do I supply authentication credentials?

Classes derived from XmlRpcClientProtocol inherit a Credentials property. This is used where the XML-RPC server authenticates the caller. The property is used in exactly the same way as the same property of the System.Net.WebRequest class. For example:

SumAndDiffProxy proxy = new SumAndDiffProxy(); 
proxy.Credentials = new NetworkCredential("jsmith","password");
SumAndDiffValue ret = proxy->SumAndDifference(2, 3);

2.7 Can I specify custom HTTP headers?

Yes, classes derived from XmlRpcClientProtocol inherit a Headers property of type WebHeaderCollection. This can be used to specify custom headers which will be added to the HTTP request. For example:

SumAndDiffProxy proxy = new SumAndDiffProxy(); 
proxy.Headers.Add("TestHeader", "this_is_a_test");
SumAndDiffValue ret = proxy->SumAndDifference(2, 3);

2.8 Why does the method name have to be hard-coded in each proxy method?

It might seem superfluous and error-prone to supply the name of the method name in each call to Invoke and indeed it is relatively trivial to code around this or even generate a proxy object automatically. However if one of these approaches were taken the client would need extra Code Access Security permissions which would prevent the XML-RPC.NET library being used unless full trust were given to the client code. The library is intended to be useful in as many situations as possible, for example with downloadable "smart", clients and so the extra effort involved in using Invoke is considered worthwhile.

2.9 How do I make an asynchronous XML-RPC request?

Although XML-RPC itself only supports synchronous method calls, XML-RPC.NET has support for asynchronous calls on proxy classes. As with synchronous calls there are twp approaches to implementing a proxy with asynchronous calls: using the XmlRpcProxyGen class to build the proxy automatically or implementing the proxy by hand.

There are two ways of handling asynchronous calls: polling to determine when the call has completed, and using a delegate to receive a callback when the call has completed.These are discussed in the following answers.

Using XmlRpcProxyGen

The interface which represents the methods of XML-RPC server endpoint is extended with Begin and End methods for each XML-RPC method to be called asynchronously. Two attributes are used for this, the XmlRpcBeginAttribute, which is used to indicate which methods used to start async calls, and the XmlRpcEndAttribute, which is used to indicate methods which end async calls. For example:

using CookComputing.XmlRpc;

struct SumAndDiffValue 
{
  public int sum; 
  public int difference; 
}

[XmlRpcUrl("http://www.cookcomputing.com/sumAndDiff.rem")] 
interface ISumAndDiff
{ 
  [XmlRpcMethod] 
  SumAndDiffValue SumAndDifference(int x, int y);
  [XmlRpcBegin]   IAsyncResult BeginSumAndDifference(int x, int y);   [XmlRpcEnd]
  SumAndDiffValue EndSumAndDifference(IAsyncResult iasr); }

The name of each Begin method is the name of the XML-RPC method name prefixed by "Begin": the XML-RPC.NET runtime code strips off the "Begin" to determine the name of the XML-RPC method to be called. In the case where the XML-RPC method name prefixed by "Begin" is not suitable, a different XmlRpcBeginAttribute constructor can be used to specify the XML-RPC method name. For example:

  [XmlRpcBegin("samples.sumAndDifference")] 
  IAsyncResult BeginSumAndDifference(int x, int y);

The name of each End method is not used in the same way - all that matters is that the parameter is IAsyncResult and the return value represents the return value of the corresponding XML-RPC method. However it is worth following following the convention that the name is similar to the name of the corresponding Being method, with "End" replacing the "Being" prefix.

Where a callback is required when the method completes it is necessary to pass a callback delegate to the method.

  [XmlRpcBegin] 
  IAsyncResult BeginSumAndDifference(int x, int y, AsyncCallback acb);

The AsyncCallback parameter must follow all the normal parameters to the method. Finally, where some state is required to be held during the async call, for example where the same callback delegate is being used to handle multiple calls and the target of the delegate has to distinguish between calls, another parameter can be specified to pass the state into the call. This is of type System.Object and must follow the AsyncCallback parameter, for example:

  [XmlRpcBegin] 
  IAsyncResult BeginSumAndDifference(int x, int y, AsyncCallback acb, object state);

If a proxy async method has been defined with the AsyncCallback parameter and/or the state parameter and they are not required in a call they can be passed as null.

Manually Coding a Proxy Class

For cases where a manually implemented proxy is require it is possible to make asynchronous calls via the BeginInvoke and EndInvoke methods of XmlRpcClientProtocol.

In both cases two new methods must be implemented in the proxy class for each XML-RPC method. For example:

[XmlRpcUrl("http://www.cookcomputing.com/sumAndDiff.rem")]
class SumAndDiffProxy : XmlRpcClientProtocol 
{
  [XmlRpcMethod("samples.sumAndDifference")] 
  SumAndDiffValue SumAndDifference(int x, int y) 
  { 
    return (SumAndDiffValue)Invoke("SumAndDifference", new Object[]{ x, y }); 
  } 
   
  public IAsyncResult BeginSumAndDifference(int x, int y, AsyncCallback callback,    
                                            object asyncState) 
  { 
    return BeginInvoke("SumAndDifference", new object[]{x, y}, callback, asyncState);    
  } 

  public Result EndSumAndDifference(IAsyncResult asr) 
  { 
    return (Result)EndInvoke(asr)[0]; 
  } 
} 

2.10 How do I poll for the result of an asynchronous call?

When polling the last two parameters of BeginSumAndDifference are not required and can be passed as null. The asynchronous call would begin like this:

IAsyncResult asr;
asr = proxy.BeginSumAndDifference(5, 3, null, null);

An instance of the IAsyncResult is returned. This has several public properties, the interesting one for polling being the boolean property IsCompleted. The value of this property can be checked as required until is found to be true, for example:

while (asr.IsCompleted == false) 
{ 
  Thread.Sleep(1000); 
} 

Once the call has completed the result of the call can be obtained by calling EndSumAndDifference, passing in the instance of IAsyncResult returned from the call to BeginSumAndDifference:

try 
{ 
  Result ret = theProxy.EndSumAndDifference(asr); 
} 
catch(Exception ex) 
{ 
  // handle the exception 
} 

Note that a try-catch block is placed around the call to EndSumAndDifference. This is because any exceptions thrown during the asynchronous processing of the call, for example a network error or because the server returned a Fault Response, are stored until the XmlRpcClientProtocol method EndInvoke is called, at which point the the exception is rethrown.

2.11 How do I receive a callback on completion of an asynchronous call?

When a callback approach is used, a method must be defined and used with a delegate to take the callback:

void SumAndDiffCallback(IAsyncResult asr) 
{ 
  try 
  { 
    SumAndDiffProxy proxy = (SumAndDiffProxy)ar.AsyncObject; 
    Result ret = proxy.EndSumAndDifference(asr); 
  } 
  catch(Exception ex) 
  { 
    // handle the exception 
  } 
} 

This time when calling BeginSumAndDifference an AsyncCallback delegete must be created and passed into the call:

AsyncCallback acb = new AsyncCallback(SumAndDiffCallback);
theProxy.BeginSumAndDifference(5, 3, acb, null); 

The IAsyncResult returned from the call is ignored because all further handling of the call is done via the callback. When the call is completed, the callback method is called and, as its implementation suggests, the original SumAndDiffProxy used to make the call is retrieved from the AsyncObject property of the IAsyncResult interface so that the EndSumAndDifference method can be called to retrieve the result.

2.12 What is the asyncState parameter used for?

The fourth parameter to BeginInvoke and the proxy methods that call this method is an object which can optionally be used to hold some state during the lifetime of the asynchronous call. In many cases this will be unnecessary and can be passed as null.

2.13 How do I implement a client in VB.NET?

The client implemented in C# in section 2.1 can be implemented similarly in VB.NET:

Imports CookComputing.XmlRpc
Module SumAndDiffSample
Public Structure SumAndDiffValue Public sum As Integer Public difference As Integer End Structure
<XmlRpcUrl("http://www.cookcomputing.com/sumAndDiff.rem")> _ Public Class SumAndDiffProxy Inherits XmlRpcClientProtocol
<XmlRpcMethod("samples.sumAndDifference")> _ Public Function SumAndDifference(ByVal x As Integer, _ ByVal y As Integer) _ As SumAndDiffValue Return Invoke("SumAndDifference", New Object() { x , y }) End Function End Class Sub Main() Dim proxy As New SumAndDiffProxy() Dim strct As New SumAndDiffValue() proxy.SumAndDifference(2, 3) End Sub End Module

2.14 Why does the following VB code cause an exception?

When using arrays in VB the following mistake is easy to make and will cause an exception at runtime:

XmlRpcMethod("db.listCollections")_
Public Function listCollections(ByVal name As String) As String()
  Dim coll() As String = {}
  Dim param(1) As Object
  param(0) = New String(name)
  coll = Invoke("listCollections", param)
  Return coll
End Function

The line defining param actually defines an array with two elements, the first element being the required string, the second element being null. It should be correctly coded as:

Dim param(0) As Object

2.15 How do I implement a client in Managed C++?

TBD

2.16How do I implement a client in JScript.NET?

TBD

2.17 Do classes derived from XmlRpcClientProtocol support the Introspection API?

Yes, support for the XML-RPC Introspection API is built into the XmlRpcClientProtocol class. Any class derived from XmlRpcClientProtocol or just an instance of XmlRpcClientProtocol can be used to make Introspection requests.

Three public methods expose the three methods of the API:

  • System.String[] SystemListMethods()
  • System.Object[] SystemMethodSignature(System.String MethodName)
  • System.String SystemMethodHelp(System.String MethodName)

SystemListMethods is used to call the system.listMethods method on the server. It simply returns an array of strings containing the XML-RPC names of the methods supported by this endpoint on the server.

string[] methods = proxy.ListMethods();

SystemMethodSignature is used to call the system.methodSignature method on the server. It returns an array of Object, each member of the array being an instance of an array of strings. This could have been represented more accurately as a jagged array - System.String[][] - but jagged arrays are not CLS compliant.

object[] signatures = proxy.SystemMethodSignature("samples.sumAndDifference"); 
foreach (string[] signature in signatures) 
  foreach (string param in signature)
Console.WriteLine(param);

Finally, SystemMethodHelp calls the system.methodHelp method on the server and returns a help string for the specified method.

string s = proxy.SystemMethodHelp("samples.sumAndDifference");

2.18 Can I define a proxy method to return void?

XML-RPC methods must always return a value. However there are some situations in which an XML-RPC method would have been defined to return void if this were possible and in these cases a dummy value may be returned, for example an empty string. The proxy method could be defined to return a corresponding type but it is perhaps more clear to specify the method as returning void, in which case the dummy return value is discarded. For example:

[XmlRpcMethod] void SumAndDifference(int x, int y);

2.19 Does XmlRpcClientProtocol.Invoke use params for the array of parameters?

When calling XmlRpcClientProtocol.Invoke, the parameter which passes the array of method parameters is defined with the params keyword. This means that instead of calling Invoke with an explcitly declared array of type object[], we can pass the method parameters as individual parameters to Invoke. For example, this proxy method:

[XmlRpcMethod] SumAndDiffValue SumAndDifference(int x, int y)
{
  return (SumAndDiffValue)Invoke("SumAndDifference", new Object[]{ x, y }); 
} 

can be coded as:

[XmlRpcMethod] SumAndDiffValue SumAndDifference(int x, int y)
{
  return (SumAndDiffValue)Invoke("SumAndDifference", x, y); 
} 

2.20 How do I specify a proxy server when making an XML-RPC request?

Sometimes the XML-RPC client is behind a firewall and must send requests via a proxy server. There are two methods of achieving this with XML-RPC.NET.

The first method is to set the Proxy property of the proxy class. In a similar way to setting the Proxy property of an instance of class System.Net.WebRequest, the property is set to an instance of the IWebProxy interface, this usually being the interface on an instance of the System.Net.WebProxy class. For example:

SumAndDiffProxy xmlRpcProxy = new SumAndDiffProxy();
xmlRpcProxy.Proxy = new WebProxy("http://proxyserver:8000");    
SumAndDiffValue ret = xmlRpcProxy->SumAndDifference(2, 3); 

The second method is to set a global default proxy for all requests by using the System.Net.GlobalProxySelection class:

GlobalProxySelection.Select = "http://proxyserver:8000";

3. Servers

3.1 What are the different ways in which an XML-RPC server can be implemented?

There are three ways of implementing an XML-RPC server using XML-RPC.NET:

  1. In IIS using a class derived from XmlRpcService.
  2. Using an XML-RPC formatter with .NET Remoting.
  3. In IIS using an XML-RPC formatter with .NET Remoting.

3.2 How do I implement an XML-RPC server in IIS?

Class XmlRpcService implements an HTTP Handler which exposes the IHttpHandler and IRequiresSessionState interfaces. When a class derived from XmlRpcService is configured via a web.config file, incoming XML-RPC requests will be directed to the handler by the ASP.NET runtime.

Implementing the Service

XmlRpcService is derived from, adding the custom application function of the Service. The derived class contains one or more public methods which represent the required XML-RPC methods. For example, the SumAndDifference example would be implemented like this:

using System; 
using CookComputing.XmlRpc; 

struct SumAndDiffValue 
{ 
  public int sum; 
  public int difference; 
}
 
class SumAndDiffService : XmlRpcService
{ 
  [XmlRpcMethod("sample.sumAndDifference")] 
  public SumAndDiffValue SumAndDifference(int x, int y) 
  { 
    SumAndDiffValue ret; 
    ret.sum = x + y; 
    ret.difference = x – y; 
    return ret; 
  } 
} 

If this code is saved to a file called sumanddiff.cs the Service can be built using the following command line:

csc /r:system.web.dll /r:CookComputing.XmlRpc.dll /target:library sumanddiff.cs  

This will build a dll assembly called sumanddiff.dll.

Configuring the Service

The Service has to be placed in a virtual directory, say xmlrpc in this case, which has a sub-directory called bin. A configuraton file called web.config is created in the virtual root directory containing the following information to specify that the Service should be invoked when a HTTP request arrives for this URL:

<configuration>
  <system.web> 
    <httpHandlers>
      <add verb="*" path="SumAndDiff.rem" 
         type="SumAndDifference, sumanddiff" />
    </httpHandlers>
  </system.web>
<configuration>

The config file specifies that if the final segment of the URL is SumAndDiff.rem the handler in the class SumAndDifference will be invoked. Note that the assembly qualified name of the class is used so that ASP.NET knows which assembly to load the class from.

The HTTP verb is specified by a wildcard. The implementation in XmlRpcService handles both POST for XML-RPC method calls and GET to return an automatically generated documentation on the Service. XmlRpcService will reject any other requests with the appropriate HTTP response code.

The extension used for the URL is “.rem”. This is for convenience because ASP.NET is configured by default to handle a number of extensions including .rem, .aspx, and .asmx. Other extensions could be used, for example .xmlrpc would be an obvious choice, but this involves changing the configuration of the virtual directory via the IIS management snap-in.

Once the service is configured a quick check can be made by pointing your browser at the URL and verifying that the automatically generated help page is displayed.

3.3 How do I implement an XML-RPC server using .NET Remoting?

XML-RPC.NET includes a Remoting formatter sink provider, the class XmlRpcServerFormatterSinkProvider. When configured this enables the Remoting infrastructure to handle incoming XML-RPC requests as well as SOAP requests.

using System;
using System.Runtime.Remoting;
using CookComputing.XmlRpc;

public struct SumAndDiffValue 
{ 
  public int sum; 
  public int difference; 
}

public class SumAndDiff : MarshalByRefObject 
{ 
  [XmlRpcMethod("sample.sumAndDifference")] 
  public SumAndDiffValue SumAndDifference(int x, int y) 
  { 
    SumAndDiffValue ret; 
    ret.sum = x + y; 
    ret.difference = x - y; 
    return ret; 
  } 
} 

Note that instead of deriving the service class from XmlRpcService, it is now derived from MarshalRefByObject.

class XmlRpcServer

{
  static void Main(string[] args) 
  { 
    RemotingConfiguration.Configure("SumAndDiff.exe.config"); 
    RemotingConfiguration.RegisterWellKnownServiceType(
	  typeof(SumAndDiff), 
      "SumAndDiff.rem",
      WellKnownObjectMode.Singleton); 
    Console.WriteLine("Press to shutdown"); 
    Console.ReadLine(); 
  } 
}

A config file is needed to specify that the Remoting infrastructure uses the XML-RPC.NET formatter:

<configuration> 
  <system.runtime.remoting>
    <application>      
      <channels>         
        <channel ref="http" port="5678">
          <serverProviders>
<formatter
type="CookComputing.XmlRpc.XmlRpcServerFormatterSinkProvider, CookComputing.XmlRpc" /> <formatter ref="soap" /> </serverProviders> </channel> </channels> </application> </system.runtime.remoting> </configuration>

Note that the default SOAP formatter is the second formatter so that if the XML-RPC formatter does not recognize the request it can pass the request onto the next formatter. The file also specifies that the server listens on port 5678.

3.4 How do I implement an XML-RPC server in IIS using .NET Remoting

A virtual directory is created and the assemblies placed in a sub-directory called bin. The config file is called web.config in this case and specifies two aspects of the server: the service element specifies the server class and its URL, and the channels element specifies that two formatters are supported, the default SOAP formatter and the XML-RPC formatter.

<configuration> 
  <system.runtime.remoting> 
    <application> 
      <service> 
        <wellknown mode="Singleton"    
                   type="SumAndDifference, sumanddiff" 
                   objectUri="SumAndDiff.rem" /> 
      </service> 
      <channels> 
        <channel ref="http">    
          <serverProviders>    
            <formatter    
type="CookComputing.XmlRpc.XmlRpcServerFormatterSinkProvider, CookComputing.XmlRpc" /> 
            <formatter ref="soap" /> 
          </serverProviders>    
        </channel>  
      </channels> 
    </application> 
  </system.runtime.remoting> 
</configuration> 

Obviously the URL used by the client should be changed to connect to IIS, for example:

SumAndDifference svr = (SumAndDifference)Activator.GetObject(
  typeof(SumAndDifference), 
"http://localhost/xmlrpc/SumAndDiff.rem");

3.5 Do XML-RPC.NET servers implement the XML-RPC Introspection API?

The base class for implementing all types of XML-RPC server is XmlRpcServerProtocol. This implements the Introspection API and so all XML-RPC.NET endpoints automatically expose this API.

3.6 What is Automatic Documentation?

XmlRpcHttpServerProtocol is the base class for implementing an XML-RPC endpoint in an HTTP server. This class handles GET requests by dynamically generating a help page for the endpoint using reflection.

The following attributes each take an optional named parameter called Description in their constructor:

- XmlRpcService
- XmlRpcMethod
- XmlRpcParameter
- XmlRpcReturnValue

The automatic documentation is generated using the method names of the class, the parameter types, and the values of the Description property of all attributes which hae been defined.

3.7 How do I support authentication in my XML-RPC service?

Authentication is not provided by standalone .NET Remoting and if required must be implemented by the developer. On the other hand IIS does support authentication and so the two IIS-based methods on implementing servers can be used where authentication is required.. Configuration is exactly the same as for normal web pages and is described in the IIS online documentation.

3.8 Can I implement a server which supports both XML-RPC and SOAP?

Yes, if using .NET Remoting and the soap formatter is configured as well as the XML-RPC formatter, the server will handle both XML-RPC and SOAP requests.

<configuration> 
<system.runtime.remoting>
<application>
<channels>
<channel ref="http" port="5678">
<serverProviders>
<formatter
type="CookComputing.XmlRpc.XmlRpcServerFormatterSinkProvider, CookComputing.XmlRpc" /> <formatter ref="soap" /> </serverProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>

3.9 Can I run XML-RPC.NET services with other web servers?

Yes - as long as the web server supports ASP.NET. This is not as implausible as it might sound because it is fairly easy to host ASP.NET using the .NET implementation of System.Web.Hosting. An example of this is the Cassini Web Server implemented by the ASP.NET team.

One point worth mentioning when running Cassini is that assemblies by default must be placed in a directory called bin under the virtual root. For example, if the URL of the service is http://localhost/xmlrpc/math.rem assemblies are loaded from virtual /bin and not virtual /xmlrpc/bin. In this example the config file is placed in the virtual /xmlrpc directory and would look like this:

<configuration>
  <system.web>
    <httpHandlers>
      <add verb="*" path="math.rem" type="MathService, MathService" />
    </httpHandlers> 
  </system.web>
</configuration>


3.10 Can I implement Services in other languages?

Services can be implemented in any CLS compliant service as the samples in this section illustrate.

VB.NET

Imports CookComputing.XmlRpc
Public Structure SumAndDiffValue
  Public sum As Integer
  Public difference As Integer
End Structure


Public Class SumAndDiffService
  Inherits XmlRpcService


 <XmlRpcMethod("samples.sumAndDifference")> _
 Public Function SumAndDifference(ByVal x As Integer, _
                                  ByVal y As Integer) _
                                  As SumAndDiffValue
   Dim ret As SumAndDiffValue
   ret.difference = x - y
   ret.sum = x + y
   Return ret
 End Function

End Class

4. Error Handling

4.1 How are XML-RPC Fault Responses represented?

An XML-RPC Fault Response contains a struct with two members: a fault code and a fault description:

<?xml version="1.0"?>
<methodResponse>
  <fault>
    <value>
      <struct>
        <member>
          <name>faultCode</name>
          <value><int>4</int></value>
        </member>
        <member>
          <name>faultString</name>
          <value><string>Too many parameters.</string></value>
        </member>
      </struct>
    </value>
  </fault>
</methodResponse>

XML-RPC.NET maps Fault Responses onto .NET exceptions, more specifically a type called XmlRpcFaultException. This is derived from System.ApplicationException and contains two properties: int FaultCode and string FaultDescription. Server application code simply throws an instance of this exception where required and XML-RPC.NET catches it and returns it as a Fault Response. For example:

throw new XmlRpcFaultException(100, "Invalid argument"); 

On the client side XML-RPC.NET receives the Fault Response, extracts the code and description, and throws a corresponding instance of XmlRpcFaultException. Client code can catch this exception using a try-catch block. For example:

SumAndDiffProxy sdprxy = new SumAndDiffProxy();
try
{
  SumAndDiffValue ret = sdprxy->SumAndDifference(2, 3);
}
catch (XmlRpcFaultException fex)
{
  Console.WriteLine("Fault Response: {0} {1}", 
    fex.FaultCode, fex.FaultString);
}

4.2 How are other types of error returned?

Error conditions can occur at other stages of an XML-RPC call. These fall into two categories, the first being networking errors, for example the client failing to connect to the server because the URL is invalid or the server is down. The second category is where the XML-RPC request reaches the server but cannot be processed for some reason, for example because it is not a valid XML-RPC request or the server application code crashes while processing the request.

Unfortunately XML-RPC does not provide a way of differentiating between an application level error and a protocol level or internal error, for example if the request is not a valid XML-RPC request. One approach is to define a well-known set of error codes which will be used by all XML-RPC implementations but this suffers from the drawback that this set of error codes cannot be guaranteed to never conflict with the error codes that application code may need to return. Application code can certainly choose its own error codes but may also need to return error codes from third party code, for example from a database.

Currently XML-RPC.NET returns a fault code of zero when a protocol or internal error occurs, leaving all other error codes available for application level error reporting.

4.3 What happens if the return value of a proxy method is incorrect?

If a proxy method is defined to return the wrong data type an instance of XmlRpcTypeMismatchException is thrown when the response is parsed. For example, if the sample.SumAndDifference proxy method was incorrectly defined to return a string:

[XmlRpcMethod] 
string SumAndDifference(int x, int y) 
{ 
  return (string)Invoke("SumAndDifference", 
                                 new Object[]{ x, y }); 
} 

an instance of XmlRpcTypeMismatchException would be thrown, containing the message "param element contains unexpected struct element". This is because the XML-RPC response contains a struct but the deserializer is expecting a string (it knows this from reflecting on the return value of the proxy method).

5. Debugging

5.1 How do I monitor XML-RPC calls across the network?

A very useful tool for monitoring the HTTP requests and responses associated with an XML-RPC call is tcptrace. This enables you to view the headers and content of HTTP requests. Configure it to use an unused port on your machine and set the destination server and destination port to those of the XML-RPC endpoint. Point your client at the local port you have chosen and all XML-RPC requests will pass through tcptrace, which will display in particular the request and response XML documents.

5.2 How do I debug an XML-RPC.NET service?

XML-RPC.NET services under IIS run within the aspnet_wp.exe process and so must be debugged by attaching a debugger to this process.

Using the SDK Debugger

To attach to aspnet_wp.exe from the .NET SDK debugger - dbgclr.exe - perform these steps:

  1. Call the service to ensure aspnet_wp.exe is running and the service is loaded.
  2. Launch the debugger.
  3. Use the File...Open File menu item to open the source file for the page you want to debug.
  4. From the Tools menu, choose Debug Processes. Check the Show system processes checkbox, if it is not checked (this may not be necessary, depending on the account aspnet_wp.exe is running under).
  5. Find the aspnet_wp.exe process and double-click it to attach to it.
  6. Close the Processes dialog.

To debug the service, place breakpoints in the source file and call the service.

Using the Visual Studio .NET Debugger

To attach to aspnet_wp.exe from Visual Studo .NET, perform the following steps:

  1. Call the service to ensure aspnet_wp.exe is running and the service is loaded.
  2. Open the project which implements the service you want to debug.
  3. From the Debug menu, choose Processes.
  4. Find the aspnet_wp.exe process and double-click it
  5. Check Common Language Runtime on the Attach to Process dialog which then appears and click OK. (It is ok to leave Native checked.)
  6. Close the Processes dialog.

To debug the service, place breakpoints in the source file and call the service.

Using Cassini

The easiest method of debugging a service is to use the Cassini web server. In either the SDK debugger or Visual Studio set the program to be run as the Cassini executable and set the arguments as required by Cassini, <physical-path> <port> <virtual-path>, for example:

c:\cassini\wwwroot   82  /

Set breakpoints and then select Debug..Start or hit F5. The advantage of using Cassini is that you don't have to re-attach the debugger each time you rebuild the service.

6. Miscellaneous

6.1 Which XML Encodings are Supported?

XML-RPC.NET uses compliant XML parsers and writers. It outputs XML in the UTF-8 encoding by default and accepts various encodings when reading XML, for example UTF-8, UTF-16, ISO-8859-1, etc.

String handling in XML-RPC is problematic because the XML-RPC standard is inconsistent in this area. XML-RPC.NET handles the issue by specifying that the string value element can contain any Unicode character which can be contained in XML text content.

When sending a request it is possible to specify the encoding by setting the XmlEncoding property of the proxy class. For example:

SumAndDiffProxy proxy = new SumAndDiffProxy();
proxy.XmlEncoding = new System.Text.ASCIIEncoding();
SumAndDiffValue ret = proxy.SumAndDifference(2, 3);

Refer to the .NET Framework documentation for the encodings that are supported.

6.2 Which Code Access Security Permissions are Required?

XML-RPC.NET needs a minimal set of Code Access Security (CAS) permissions, in particular networking permission to connect to a server. In the case of downloadable smart clients this could be restricted to connecting to the server from which the client was downloaded.

6.3 Why is the key file not included in the distribution?

Building XML-RPC.NET from the distribution will fail because of a missing file called CookComputing.key. This contains the public/private key pair used to sign the CookComputing.XmlRpc.dll assembly with a strong name. The file is not distributed because this would enable anyone else to build a malicious version of the assembly and pass it off as originating from Cook Computing.

To generate your own key file using the sn.exe tool as follows:

sn -k mykeyfile.snk

and modify the relevant line in assemblyinfo.cs:

[assembly: AssemblyKeyFile("mykeyfile.snk")]

Alternatively, comment out this line if you do not need to sign your assembly with a strong name.

6.4 How do I verify an XML-RPC.NET assembly is genuine?

Use the sn.exe tool to extract the public key token from the assembly:

sn -T cookcomputing.xmlrpc.dll

The token should be the same as this if the assembly was built using the Cook Computing key file:

a7d6e17aa302004d

6.5 Can I define an interface from which both the proxy and server classes are derived?

Yes, as of version 0.7.0 of XML-RPC.NET this works.

7. Resources

7.1 XML-RPC Specification

Dave Winer's XML-RPC specification is available here.

7.2 Books and Tutorials

The following book is worth reading if you are working with XML-RPC. As well as clearing up some of the issues in the spec it includes chapters on several of the XML-RPC toolkits.

Programming Web Services with XML-RPC - Simon St. Laurent, Edd Dumbill, Joe Johnston (O'Reilly)

7.3 Websites

XML-RPC.Com - specfications, implementations, services, etc.

XML-RPC.NET Site - home of XML-RPC.NET

7.4 Mailing Lists

There are two relevant Yahoo groups:

xml-rpc - " This list provides a forum for discussing and implementing the XML-RPC specification as a cross-platform protocol."

XMLRPCNET - "Discussion of the XML-RPC.NET library."

7.5 Articles and Tutorials

Eric Kidd's XML-RPC HOWTO provides useful information on implementing clients and servers in a variety of languages.


'.Net General > .Net XML-RPC' 카테고리의 다른 글

XML RPC 관련 Site  (0) 2011.11.17
XML-RPC HOWTO  (0) 2011.11.17
닷넷에서의 XML-RPC 구현 (클라이언트)  (0) 2009.09.18
닷넷에서의 XML-RPC 구현 (설정)  (0) 2009.09.18
닷넷에서의 XML-RPC 구현 (서버)  (0) 2009.09.18

관련글 더보기