Introduction
There are various examples of ReST available on the net like Web-Services for .NET/C#, but none of them provide a simple .NETish framework. For example, how easy is it to create a Web-Service by just deriving from a class and annotating methods as exposed for the web? This article provides a small framework for ReST based Web-Services primarily targeted for .NET versions 3.5 and older.
Using the code
Like any other article on the web about ReST and .NET, this implementation involves handling specific resources request through an IHttpHandler
; in this case, it is PoC.Web.Services.ReSTServiceHandler
. This IHttpHandler
would handle the ReST calls (Get, Put, Post, and Delete) and dispatch them appropriately. In order to add the handler, the following lines must be added to the web.config:
<system.web>
....
....
<httpHandlers>
....
<add verb="*" path="RESTService/*"
type="PoC.Web.Services.ReSTServiceHandler, PoC.Web.ReSTService"/>
<add verb="*" path="RESTService/*/*"
type="PoC.Web.Services.ReSTServiceHandler, PoC.Web.ReSTService"/>
</httpHandlers>
</system.web>
The library provides two mechanisms for ReST Web-Services:
IReSTService
Same as the Web-Service model. Annotate all the methods of a class which need to be exposed.
IReSTFulService
Provides four calls to handle a request: Get, Put, Post, and Delete.
IReSTService based Web-Service
An example of an IReSTService
is shown below:
public class Calculator : PoC.Web.Services.IReSTService
{
[PoC.Web.Services.ReSTMethod(PoC.Web.Services.HttpVerb.Get)]
public int Sum(int a, int b)
{
return a + b;
}
[PoC.Web.Services.ReSTMethod(PoC.Web.Services.HttpVerb.Get)]
public int Multiply(int a, int b)
{
return a * b;
}
[PoC.Web.Services.ReSTMethod(PoC.Web.Services.HttpVerb.Get)]
public int Subtract(int a, int b)
{
return a - b;
}
}
In this example, we have a Calculator
class which implements IReSTService
and exposes three methods by annotating them with the ReSTMethod
attribute. The ReSTMethod
attribute requires an allowed verb on that method. That's it. If built, the CalculatorService
can now be accessed by these URLs:
- Sum URL: http://<servername/localhost>/<web app>/RESTService/Calculator/Sum?a=-1&b=2
- Multiply URL: http:/<servername/localhost>/<web app>/RESTService/Calculator/Multiply?a=195&b=21
- Subtract URL: http:/<servername/localhost>/<web app>/RESTService/Calculator/Subtract?a=-12&b=2
Here is another example:
public class ShowNTell : PoC.Web.Services.IReSTService
{
XmlDocument dom = new XmlDocument();
public ShowNTell()
{
dom.LoadXml("<Root><Child><SubChild>" +
"<Parameter></Parameter><MagicNumber>" +
System.Guid.NewGuid().ToString("N") +
"</MagicNumber></SubChild>" +
"</Child></Root>");
}
[PoC.Web.Services.ReSTMethod(PoC.Web.Services.HttpVerb.Get)]
[PoC.Web.Services.ReSTXmlResponse]
public Employee[] Object2Xml()
{
Employee e1 = new Employee();
e1.Name = "Aremac Nokin ";
e1.Id = "11201";
e1.Address = "123 Summer Drive";
e1.City = "Brahman";
e1.Zip = "00001";
e1.JoiningDate = DateTime.Today.AddYears(-5).AddMonths(-3).AddDays(-20);
e1.Salary = 100000.00;
Employee e2 = new Employee();
e2.Name = "Reyalp Sinnet";
e2.Id = "11201";
e2.Address = "123 Summer Drive";
e2.City = "Brahman";
e2.Zip = "00001";
e2.JoiningDate =
DateTime.Today.AddYears(-2).AddMonths(-1).AddDays(-40);
e2.Salary = 200000.00;
return new Employee[] { e1, e2 };
}
[PoC.Web.Services.ReSTMethod(PoC.Web.Services.HttpVerb.Get)]
public XmlNode ReturnNode(string some_param)
{
dom.SelectSingleNode("//Root/Child/SubChild/Parameter").InnerText = some_param;
return dom.SelectSingleNode("//Root/Child/SubChild");
}
[PoC.Web.Services.ReSTMethod(PoC.Web.Services.HttpVerb.Get)]
public XmlDocument ReturnDom(XmlDocument param_1, int param2)
{
dom.SelectSingleNode("//Root/Child/SubChild/Parameter").InnerText =
param2.ToString();
if (param_1 != null && param_1.DocumentElement != null)
{
XmlElement elem = dom.CreateElement("NewItem");
elem.InnerXml = param_1.DocumentElement.OuterXml;
dom.SelectSingleNode("//Root/Child").AppendChild(elem);
}
return dom;
}
}
In this example, we are exposing an array of POCO, an XmlNode
, and an XmlDocument
. We also see a new attribute in this example, ReSTXmlResponse
. This attribute makes the engine (IHttpHandler
) return an XML representation of the object by serializing the object. If the return type of the method is typeof XmlNode
, then the engine would return the OuterXml
of the node and set the content-type to "xml". And yes, there is a special type converter for XmlDocument
/XmlNode
from string
;-).
IReSTFulService based Web-Service
This interface has the following methods:
object GetCall(string args, HttpContext context)
object PutCall(string args, HttpContext context)
object PostCall(string args, HttpContext context)
object DeleteCall(string args, HttpContext context)
An example is shown below:
public class Parts : PoC.Web.Services.IReSTFulService
{
....
....
#region IReSTFulService Members
public object GetCall(string arg, HttpContext context)
{
if (string.IsNullOrEmpty(arg))
{
return allParts;
}
else
{
return filteredParts;
}
}
public object PutCall(string arg, HttpContext context)
{
Part ps = null;
try
{
ps = PoC.Web.Services.Utilities.TypeConversion.
Deserialize<part>(context.Request.InputStream);
}
catch(Exception ex){
throw new PoC.Web.Services.InternalErrorException(503, "Bad Stream");
}
return UpdatePart(ps);
}
public object PostCall(string arg, HttpContext context)
{
Part ps = null;
try
{
ps = PoC.Web.Services.Utilities.TypeConversion.
Deserialize<part>(context.Request.InputStream);
}
catch (Exception ex)
{
throw new PoC.Web.Services.InternalErrorException(503, "Bad Stream");
}
return InsertPart(ps);
}
public object DeleteCall(string arg, HttpContext context)
{
throw new PoC.Web.Services.InternalErrorException(503, "Method Not Supported");
}
#endregion
}
The "Parts" service can now be accessed through the following links:
- Link to get all Parts:
URL: http://<Servername/localhost>/<Web App name>/RESTService/Parts
HTTP method: Get
- Link to get a Part by ID:
URL: http://<Servername/localhost>/<Web App name>/RESTService/Parts/1101
HTTP method: Get
- Update a Part:
URL: http://<Servername/localhost>/<Web App name>/RESTService/Parts
HTTP method: Put
Send Body: '<Part><Id>1103</Id><Name>somet part name</Name><Description>something ...</Description></Part>'
- Create a new Part:
URL: http://<Servername/localhost>/<Web App name>/RESTService/Parts
HTTP method: Post
Send Body: '<Part><Id></Id><Name>somet part name</Name><Description>something ...</Description></Part>'
- Delete a Part:
URL: http://<Servername/localhost>/<Web App name>/RESTService/Parts/1103
HTTP method: Delete
Send Body: '<AuthorizationCode></AuthorizationCode>'