Click here to Skip to main content
Click here to Skip to main content

Build ReST based Web Services in .NET/C#

By , 12 Jul 2009
 

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:

  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:

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:

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; // 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)

About the Author

Parag.Gadkari
United States United States
Member
Loves coding...

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralHosting in Windows ServicemembervanjaESS25 Jan '11 - 6:08 
Is there a way to host Calculator in Windows service?
GeneralMy Vote of 5memberSluggish14 Dec '10 - 10:15 
I quickly made some mods to support passing a Syndicated Feed (Atom) as a POST stream - and it worked like a charm.
 
Thanks for the great code!
 
Best,
Ross Anderson
QuestionSIX different classes = simple???membermoonfly33322 Sep '10 - 13:35 
"...simple .NETish framework..."
if ur sample code spans more than couple of files, don't tag it simple. someone might just be starting to explore something new, and have to go through SIX different classes/interfaces, including an interface that implements another interface, and you tag it simple!! wasted 10 valuable minutes!!
AnswerRe: SIX different classes = simple???memberAdamNThompson28 Jan '12 - 16:57 
Simple enough for me... Great article!
-Adam N. Thompson
adam-thompson.com

GeneralAny body knowsmemberrobertox200422 Jul '10 - 23:12 
How i can translate the code of the examples in to VB.NET?
 

Thanks
AnswerRe: Any body knowsmemberthatraja10 Aug '10 - 21:21 
Try this
 
http://www.sharpdevelop.net/[^]

GeneralCannot deploy in IIS 7memberTillmanZ23 Feb '10 - 19:32 
Hi there,
 
running the solution in VStudio 2010 (with the built in cassini web server) works great and just as I have hoped.
However when I deploy the project into IIS7 (create Web-Site and point to folder where the default.aspx and the bin directory are located) it will not work.
More precisely the RestServiceHandler is never being called. I have added some debug prints and all I get is nothing. Smile | :)
So what do I need to do in order for IIS 7 to really start using the code?
 
By the way - I have modified the web.config accordingly. So it currently has two entries with "add verb path="RestService/*" and the other with "add verb path="RestService/*/*".
 
Again, everything works fine locally in VStudio only IIS7 does not want to jump into the code.
 
Thank you very much for your support!
 

Cheers
GeneralAssigning Session ID REST Web ServicememberMember 116029618 Feb '10 - 6:02 
Hi,
 
It's working fine for me till i plan to access the Session in this.
 
Instead of returning the number "C" in Sum (A+B) Function. I try to send
 
c= Session.SessionID. It gives the error
 
Session state can only be used when enableSessionState is set to true, either in a configuration file or in the Page directive. Please also make sure that System.Web.SessionStateModule or a custom session state module is included in the <configuration>\<system.web>\<httpModules> section in the application configuration.
 
As per the exception i have all the session true.
 
Please help....
GeneralProblem in deploying REST service in IISmemberjimmyjos16 Oct '09 - 2:02 
Hi,
 
I was trying to deploy the service on IIS and access it from the application. But it says page not found. Can you please help on how to host this in IIS and access it from an application.
 
thanks in advance,
 
Jimz.

GeneralRe: Problem in deploying REST service in IISmembermdodich18 Nov '09 - 8:40 
Sounds like my problem is similar.
 
After deploying on IIS I can get here...
http://server/app/default.aspx
 
But not here...
http://server/app/RESTService/Calculator/Sum?a=-1&b=2
 
Anyone know why?
GeneralRe: Problem in deploying REST service in IISmemberRafael Ferraro18 Jan '10 - 8:33 
You ever get anywhere with this? I am having same problem.
GeneralRe: Problem in deploying REST service in IISmembermdodich19 Jan '10 - 3:05 
No I dropped it for awhile. Please post here if you find the answer though!   Thanks   Smile | :)
GeneralRe: Problem in deploying REST service in IISmembergoldlaser11 Feb '10 - 12:44 
I found the solution for this problem from Ian Robinson's article,
http://serverfault.com/questions/102695/iis6-wildcard-mapping-to-asp-net-no-file-extension-results-in-iis-404[^]
 
Solution from Ian Robinson:
 
IIS 6.0 - Windows 2003 Server
 
open property page for website / virtual directory.
click the 'home directory' tab
click the 'configuration' button, select the 'mappings' tab
click 'insert' next to the 'Wildcard application maps' section
browse to the aspnet_isapi.dll (normally at c:\windows\microsoft.net\framework\v2.0.50727\aspnet_isapi.dll)
Ensure that 'check that file exists' is unchecked
Click OK, OK, OK to close and apply changes
Generaldirectly aceesing using URL in IEmemberraam_kimi26 Aug '09 - 2:20 
thanks for the great articel..
i can be able to access the rest service by just passing this URL in IE http://localhost:1055/RESTService/Parts.. but how do i invoke the SUM method in calculator...
 
is that possible..?
 
Thanks
Ramesh Vel
 
Its Just Zeros and Ones

GeneralRe: directly aceesing using URL in IEmemberraam_kimi26 Aug '09 - 2:24 
hey sorry,
i got it.. dint read the article fully... Frown | :(
 
Its Just Zeros and Ones

QuestionIIS7memberKofi Sarfo13 Jul '09 - 20:32 
Parag, thanks for the code example. To run this with IIS7 the Application Pool needs to be changed to Classic .NET AppPool, however, there's a 404 when trying to deploy and run this from IIS rather than via the ASP.NET Development Server (Cassini). So the next thing to do then is to add Handler Mappings.
 
Kofi Sarfo
modified on Tuesday, July 14, 2009 5:27 AM

AnswerRe: IIS7memberParag.Gadkari14 Jul '09 - 17:12 
Kofi: I tested this against IIS6, Cassini and WebDev and they all work fine. I am not sure why AppPool setting would need to be changed in IIS7 but I can give it a shot.
 
Also, can you tell me what URL results in 404?
 
Regards,
Parag
 
"Brahmana satyaṃ jagat mithyaa, jiyvo brahmaiva naaparah" -- Adi Shankara
"Civilization is the limitless multiplication of unnecessary necessities." -- Mark Twain

GeneralRe: IIS7memberEldiablohijo30 Jul '09 - 3:48 
IIS7 Requires different Web Config Entries.
 
<system.webServer>
    ....
    ....
    <handlers>
        ....
        <add name="PoCREST" verb="*" path="RESTService/*" 
          type="PoC.Web.Services.ReSTServiceHandler, PoC.Web.ReSTService"/>
        <add name="PoCREST2" verb="*" path="RESTService/*/*" 
          type="PoC.Web.Services.ReSTServiceHandler, PoC.Web.ReSTService"/>
    </handlers>
</system.webServer>
 
The only thing that is different is the root nodes and then there is an additional name attribute, which can be whatever.
GeneralRe: IIS7membermamidrx11 May '12 - 8:47 
Hello All,
I did add the handlers, but when I type the URL in the webbrowser, it returns nothing.. I am using IIS7... But when I run in VS2010, I get the results back... Is there anything extra I need to do to work in IIS7
 
Thanks,
Ravi

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 12 Jul 2009
Article Copyright 2009 by Parag.Gadkari
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid