Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / IIS

Build ReST based Web Services in .NET/C#

5.00/5 (12 votes)
12 Jul 2009CPOL2 min read 125.7K   5.5K  
A ReST based Web Service for C#.

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:

XML
<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:

  1. IReSTService
  2. Same as the Web-Service model. Annotate all the methods of a class which need to be exposed.

  3. IReSTFulService
  4. Provides four calls to handle a request: Get, Put, Post, and Delete.

IReSTService based Web-Service

An example of an IReSTService is shown below:

C#
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:

  1. Sum URL: http://<servername/localhost>/<web app>/RESTService/Calculator/Sum?a=-1&b=2
  2. Multiply URL: http:/<servername/localhost>/<web app>/RESTService/Calculator/Multiply?a=195&b=21
  3. Subtract URL: http:/<servername/localhost>/<web app>/RESTService/Calculator/Subtract?a=-12&b=2

Here is another example:

C#
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:

C#
public class Parts : PoC.Web.Services.IReSTFulService
{
    ....
    ....
    #region IReSTFulService Members

    public object GetCall(string arg, HttpContext context)
    {
        if (string.IsNullOrEmpty(arg))
        {
            
            return allParts; // typeof Parts[]
        }
        else
        {
            return filteredParts; //typeof Parts[]
        }
    }

    public object PutCall(string arg, HttpContext context)
    {
        Part ps = null;
        //Get Part
        try
        {
          ps = PoC.Web.Services.Utilities.TypeConversion.
                   Deserialize<part>(context.Request.InputStream);
        }
        catch(Exception ex){ //Bad Stream
                throw new PoC.Web.Services.InternalErrorException(503, "Bad Stream");
        }
        return UpdatePart(ps);// let's say string
    }

    public object PostCall(string arg, HttpContext context)
    {
        Part ps = null;
        //Get Part
        try
        {
            ps = PoC.Web.Services.Utilities.TypeConversion.
                    Deserialize<part>(context.Request.InputStream);
        }
        catch (Exception ex)
        { //Bad Stream
            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:

  1. Link to get all Parts:
  2. URL: http://<Servername/localhost>/<Web App name>/RESTService/Parts

    HTTP method: Get

  3. Link to get a Part by ID:
  4. URL: http://<Servername/localhost>/<Web App name>/RESTService/Parts/1101

    HTTP method: Get

  5. Update a Part:
  6. 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>'

  7. Create a new Part:
  8. 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>'

  9. Delete a Part:
  10. URL: http://<Servername/localhost>/<Web App name>/RESTService/Parts/1103

    HTTP method: Delete

    Send Body: '<AuthorizationCode></AuthorizationCode>'

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)