Keeping web services simple

by Mads Kristensen 8. November 2007 04:27

The easy way for .NET developers to create an API over HTTP is to use SOAP web services. It’s natively supported by .NET and has rich support for more complex data types such as the DataSet. The problem with SOAP is that it isn’t well supported by other platforms and to consume a .NET SOAP web service in PHP is a nightmare.

If you want to build an API for your web application over HTTP, then consider a simpler approach that is much faster, supports both GET and POST, can easily be RESTful, and works on all platforms. XML-RPC is a good way of doing such a service, but we can do it even simpler and define our own contract easily.

The class

I’ve build a very small class, RpcService, that let’s you pass plain text both ways over HTTP. The text will probably be XML for a more structured contract, but that’s up to you. The class has a static Send method and a static Receive method.

The Send method passes the plain text or XML to the request stream and returns the reply as a string. You can then parse the XML your self if needed. The Receive method listens for requests with text in the request stream and returns it as a string. Now you have both ends of the API – the client and the server.

Example

Let’s say you want to build a stock quote service that returns the quote of a given stock based on the a stock symbol. You then start by adding an HttpHandler to your website called stock.ashx. That file will function as your web service end point. In the handler’s ProcessRequest method you then call the Receive method to get the text from the request stream. It contains the stock symbol and now you need to respond with the current stock quote. You simply just write the quote using Response.Write in order to return it. It could look like this:

public void ProcessRequest(HttpContext context)

{

  string symbol = RpcService.Receive();

  string qoute = DataBase.GetQuote(symbol);

  context.Response.Write(qoute);

}

Now you have the service ready on the server. The only thing missing is a way to consume the service . In order to do that we need to use the Send method of the RpcService class. It could look like this:

Uri url = new Uri("http://api.example.com/quote.ashx");

string symbol = "MSFT";

string quote = RpcService.Send(url, symbol, RpcService.HttpMethod.POST);

What happens here is that we now send the stock symbol in the request stream over HTTP to the stock.ashx service and retrieves the content of the response. The circle is now complete, we have a custom web service.

Use XML

Now you might think why in the world we need the RpcService when the above example could just use a WebClient to download the content and pass it a query string with the symbol. Well, you need it when you want to pass more complex data to the stock service. In that case it will be smart to use XML as the data format.

You will be able to do a complex query against the stock service that specifies at what dates you want the stock quotes returned from. Consider writing an XML document that looks like this and then pass it to the Send method:

<stock symbol="MSFT">
  <date>2007-11-07</date>
  <date>2007-11-06</date>
  <date>2007-11-05</date>
  <date>2007-11-04</date>
  <date>2007-11-03</date>
  <date>2007-11-02</date>
  <date>2007-11-01</date>
</stock>

Then the XML will be written to the request stream and delivered to the stock.ashx service. The service could then iterate over the XML document to retrieve all the dates and return a response also in XML. It could look like this:

public void ProcessRequest(HttpContext context)

{

  string request = RpcService.Receive();

  System.Xml.XmlDocument doc = new System.Xml.XmlDocument();

  doc.LoadXml(request);

 

  string symbol = doc.SelectSingleNode("stock").Attributes["symbol"].InnerText;

  System.Xml.XmlNodeList list = doc.SelectNodes("//date");

 

  context.Response.Write("<stock>");

 

  foreach (System.Xml.XmlNode node in list)

  {

    DateTime date = DateTime.Parse(node.InnerText);

    string quote = DataBase.GetQuote(symbol, date);

    context.Response.Write("<quote date=\"" + date + "\">" + quote + "</quote>");

  }

 

  context.Response.Write("</stock>");

}

All this you could easily do in .NET using a SOAP web service, but this is much faster, much smaller and 100% cross platform compatible. You could use other formats instead of XML if you want. JSON objects for use in AJAX calls for instance.

The RpcService class

The static RpcService class exposes two public methods – Send and Receive. It also has a single private method and an enum for specifying the HTTP method. In total, it is 78 lines long including comments. You need to include the class on both the client and the server. For the server, you can just drop it into the App_Code folder. You can do the same on the client if it’s also a web application.

Download RpcService.zip (836 bytes)

* Only $4.95/month ASP.NET & Windows 2008 + IIS 7 Hosting! FREE SQL Included

Tags: , , ,

ASP.NET

Comments

11/8/2007 6:19:34 AM #

Claus

Nice one..
Regarding PHP - use PHP5's internal. Others like Pear/Soap seem to have some issues regarding SOAP headers.

Claus Denmark |

11/8/2007 6:43:32 PM #

Richard

I've been reading articles like this recently.  I agree, SOAP based web services are heavy weight and have some nice features, if that's what your interested in, but are bloated for most types of use.  Particularly when building a web 2.0 style app.  You've suggested a lovely, simple, alternative.

As a side note I'm consuming a .net 2 web service from Perl at the moment and finding it rather easy.  The biggest thing that held me up was the need for a SOAPAction header.

This is a blog I was looking at only today on the same subject. kevinup.wordpress.com/.../

Richard United Kingdom |

11/9/2007 3:28:35 PM #

Chicken

In regard to PHP - we've been using nuSOAP in my previous company and it was a piece of cake to use .NET web services with this PHP lib. Of course I'm talking only about basic service without extensions (and returning simple objects and object collections not data sets Smile

But your example is also a good one. To be honest I have asked myself many times why web service is better then simply accessing some endpoint with sockets (from PHP). The advantage of web service is that client can browse the service, check the methods accessible etc. also for many platforms adding Web Reference is a piece of cake and it automatically generates proxy classes.

Chicken Poland |

11/12/2007 4:20:01 AM #

pingback

Pingback from rtipton.wordpress.com

Week Link Post 15 « Rhonda Tipton’s WebLog

rtipton.wordpress.com |

11/13/2007 9:19:18 PM #

Frank Rau

This is a nice cross-platform solution.  I recently had to integrate .NET web services into a classic ASP site and had the luxury of being able to install the SOAPToolkit for SOAP parsing developer.franklinrau.com/.../...ic-ASP-Sites.aspx (e.g. not viable for hosted solution).  From what I see the class does nothing more than a simple POST to a Url that can be achieved by using the HttpWebRequest class and gathering of form values from the host.  I also agree with Chicken in that using this is a disadvantage to .NET clients who will not be able to discover the web service methods.

Frank Rau United States |

11/16/2007 6:31:01 AM #

Justin Etheredge

I know it has been said a million times, but calling them SOAP (Simple Object Access Protocol) web services is an oxymoron considering how complex the SOAP spec is. I agree with you 100% that web services are way too complicated and horribly slow. I think that REST web services are certainly a good direction, but I also think that we need to define some standards for access across web service implementations. And by that I mean that we do need some sort of authentication, reliable message delivery, security, routing, etc... I just think that all of the WS-* standards have just piled complexity on top of an already complex standard. But how do we incorporate these kinds of things in a simple manner? Maybe we can't. But now I am rambling.

Justin Etheredge United States |

12/29/2007 12:56:47 AM #

Thomas H. Lanier

Do you know how to modify your Send method to support Internet authentication (user/password).

For instance this does not seem to work.

request.Credentials = new NetworkCredential("user", "password");

Thanks

Thomas H. Lanier United States |

Comments are closed

About the slave

Mads Kristensen Mads Kristensen
Web developer at ZYB and founder of BlogEngine.NET. More...

LinkedIn ZYB Facebook Last.fm Twitter View Mads Kristensen's profile on Technorati

The Lounge

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008