Uncategorized

Developing a REST Web Service using C# – A walkthrough

REST stands for Representational State Transfer. The term was introduced by Roy Fielding in his doctorial dissertation. REST is an architectural style for designing networked applications. It is an alternate to using complex mechanisms like COBRA, RPC, and SOAP to connect between client and server. REST makes communication between remote computers easy by using the simple HTTP protocol which support for CRUD (Create, Read, Update, and Delete) operations on the server. In a way, our World Wide Web is also based on the REST architecture.

In a nutshell, REST is based on the representation of resources. A resource is an object that we can access over the World Wide Web. It can be a document, an image, or anything. When we request a resource (an object) from a server, the server returns the representation of that resource. In REST architecture, a client requests an operation (CRUD) on an object (resource) on the server, and the server returns data as per requirements.

Example – Let us consider a Web Service that returns Employee information. The Web Service responds to client calls by polling a database and returning a result. In classical Web Services or WCF Services, we would have a method exposed to clients, like GetEmployee(). The REST architecture is different from this as it does not work with the methods but with nouns (resources / objects) and allow verbs (HTTP Methods) to operate on them.

So if we want to get Employee information, we send an HTTP GET on the object Employee rather than query a method like GetEmployee(). The advantage is manifold, the most important being the power and flexibility that we get in our hands to play around with the object.

For further reading, see the References section at the end of the article. I will not go into the details of explaining REST, rather concentrate on how to code one from scratch.

Why use REST?

Let us consider a practical example. Imagine that you have to build a Web Service which should be cross functional and should be able to be consumed by any client. Traditional Web Services in .NET have to be consumed using a proxy. What if the client does not know how to create a proxy? Imagine that your Web Service needs to be consumed by a client running on an iPhone or Android. As far as I know, Objective C does not know how to create a proxy. It only knows to send basic HTTP verbs – GET, PUT, POST, and DELETE – to the server and expect XML or string as response. Enter the world of REST!!

 

HTTP verbs

At this time, it is worthwhile to revise our basic HTTP verbs. I will just give a brief introduction. Detailed information is easily available over the internet.

GET – GET is one of the simplest HTTP methods. Its main job is to ask the server for a resource. That resource may be an HTML page, a sound file, a picture file (JPEG) etc. We can say that GET method is for getting something from the server. In GET method, the data we send is appended to the URL.

Example:

GET /path/file.html HTTP/1.0
From: someuser@jmarshall.com
User-Agent: HTTPTool/1.0

POST – The POST method is used to submit data to be processed to the identified resource. The data is included in the body of the request. This may result in the creation of a new resource, or the updates of existing resources, or both.

Example:

POST /path/script.cgi HTTP/1.0
From: frog@jmarshall.com
User-Agent: HTTPTool/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 32

home=Cosby&favorite+flavor=flies

PUT – The PUT method is used to submit data to be updated to the identified resource. Like the POST method, the data is included in the body of the request.

DELETE – The DELETE method is used to delete a specific resource.

 

HTTP handlers

We will be making use of HTTP handlers to configure our REST Web Service. HTTP handlers give us the power to configure our own web extensions by configuring the ISAPI filters in IIS. To explain briefly, the following happens under the hood:

IIS listens for any incoming requests on port 80 (default). When it receives a request for a web page, it invokes a web application. This routing of this request to the web application is done via ISAPI filters. For example, if it receives a request for a page called Default.aspx, a DLL called aspnet_isapi.dll actually goes and invokes the page Default.aspx. The Machine.Config file will have the mapping which will tell IIS how to handle a .aspx web request. Users can have their own calls – like .codeproject – by configuring the ISAPI filters of IIS. This is done through HTTP handlers. So, if I want to make a call, say, Default.codeproject, instead of Default.aspx, it would have to be done through HTTP handlers. More information about this can be found in the References section of this article. HTTP handlers is the key to build REST Web Services. We will see later how our Employee object will be exposed using HTTP handlers.

 

The project

To understand REST, let’s dig into a sample project and see what is happening under the hood. The project involves building a sample client – server database application which returns employee information. From the definition of REST above, we can conclude the following:

  1. The resource here will be the employee (object) which will be exposed to the client.
  2. The client should be able to perform CRUD operations (basic HTTP verbs) on the resource.

We have to design a system which will accomplish the above tasks. The project can be divided into the following:

  1. Create a simple database which contains an Employee table.
  2. Create a simple Employee class as a placeholder for the database employee information.
  3. Code basic database operations like Read, Update, Delete, Insert for Employee.
  4. Create a REST Web Service by exposing Employee to HTTP verbs.
  5. Deploy the application.
  6. Test the application.

It is time to define the way the client will be communicating with the server. The data exchange happens through XML format as it is widely accepted across all platforms.

  1. Get information about an employee – HTTP GET method. The server expects an HTTP GET method with the employee code passed in the query string. It then returns the employee information for that particular employee from the database in the form of XML.

    Example: https://localhost/RestWebService/employee?id=3550.

  2. Insert a new employee – HTTP POST method. The server expects an HTTP POST method to create a new employee. The client is required to pass the employee information as XML in the message body. The server then reads the message body and creates a new employee information in the database.

    Example – POST/employeeHTTP/1.1 Host: https://localhost/RestWebService

  3. Update an existing employee – HTTP PUT method. The server expects an HTTP PUT method to update an existing employee. The client is required to pass the information for the employee who is getting updated as XML in the message body. The server then reads the message body and updates the particular employee in the database.

  4. Delete an existing employee – HTTP DELETE method. The server expects an HTTP DELETE method to delete a particular employee from the database. The server expects the employee code for the employee to be deleted, in the query string of the URL.

    Example: https://localhost/RestWebService/employee?id=3550.

Thus we can see that by the above design, we have successfully exposed a state of an object – in this case, Employee – to the client applications calling it. As mentioned above, the objective of REST is to make the HTTP verbs operate on the nouns. The HTTP verbs here are GET, POST, PUT, and DELETE (CRUD), as explained above. The noun here is the resource Employee. If we can achieve this, we would have created a basic REST Web Service.

The code

Part # 1 – Create a simple database which contains an Employee table

This one is easy. You have to create a simple database called Company and create a table called Employee, as shown above. In the source code provided, I have included the scripts necessary to create a database and also create an Employee table. I have also provided scripts to insert some dummy data to get us started. Please look in the EmployeeDataBase project under the script folder. All you need to do is copy and run the scripts, and you should be all set.

Note: This will work with SQL Server 2008. You have to make adjustments if you are using any other version of SQL Server or any other database.

Let us also see the structure of the table that will hold the employee information. Below is the snapshot.

image001

Part # 2 – Create an Employee class as a placeholder for the database employee information

I have this in a class library project called Company. I have a class Employee which has the following properties:

  • FirstName
  • LastName
  • EmpCode
  • Designation

Let us see the structure of the Employee class:

<Employee>
<FirstName>Anshu</FirstName>
<LastName>Dutta</LastName>
<EmpCode>3550</EmpCode>
<Designation>SSE</Designation>
</Employee>

The XML is quite self explanatory. It has these attributes: first name, last name, employee code, and designation.

Refer the code below. The code again is self explanatory. We have defined the public properties of the class:

public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
public int EmpCode
{
get { return _empCode; }
set { _empCode = value; }
}
public string Designation
{
get { return _designation; }
set {_designation = value;}
}
public string getEmployeeName()
{
string fullName = FirstName + ‘ ‘ + LastName;
return fullName;
}

Part # 3 – Code basic database operations like Read, Update, Delete, Insert for Employee

This one includes coding of basic database operations namely – Update, Insert, Select, Delete.

I have a Data Access Layer specifically for this. It is called DAL, and it is again a set of class libraries. It has a class DAL which has the code for the database operations.

The constructor of the class takes the connection string and connects to the database.

public DAL(string _connString)
{
err = new ErrorHandler.ErrorHandler();
connString = _connString;
}

Database operations

I have used parameterized queries in all the cases as they are efficient.

Code for Insert operation:

try
{
using (conn)
{
//using parametirized query
string sqlInserString =
"INSERT INTO Employee (FirstName, LastName, ID, " +
"Designation) VALUES (@firstName, @lastName, @ID, @designation)";

conn = new SqlConnection(connString);

command = new SqlCommand();
command.Connection = conn;
command.Connection.Open();
command.CommandText = sqlInserString;

SqlParameter firstNameparam = new SqlParameter("@firstName", emp.FirstName);
SqlParameter lastNameparam = new SqlParameter("@lastName", emp.LastName);
SqlParameter IDparam = new SqlParameter("@ID", emp.EmpCode);
SqlParameter designationParam = new SqlParameter("@designation", emp.Designation);

command.Parameters.AddRange(new SqlParameter[]{
firstNameparam,lastNameparam,IDparam,designationParam});
command.ExecuteNonQuery();
command.Connection.Close();
}
}
catch (Exception ex)
{
err.ErrorMessage = ex.Message.ToString();
throw;
}

I have a query string which takes a parameterized query. For the insert statement, the parameters are first name, last name, employee code, and designation. I am then declaring SQL parameters for these and adding them to the command parameter collection. Then I execute the command using command.ExecuteNonQuery().

Other operations like Update, Delete, and Select are similar. For the Select operation, the method GetEmployees() returns an Employee class.

One thing that is worth mentioning is that the DAL class has a method called GetException(). This method passes on whatever exception that the method encounters to the calling client. I will explain the importance of this method in subsequent stages.

public string GetException()
{
return err.ErrorMessage.ToString();
}

Part # 4 – Create the REST Web Service

Now that we have set up the foundation for our Web Service, let’s dig deep into the code that actually implements the REST architecture. As explained earlier, REST is about exposing nouns (objects) to verbs. Any normal request to an ASP.NET Web Server looks like this: https://localhost/myWbPage.aspx.

Remember here, we want to expose the object Employee and not the page. Note that the operation can also be achieved by calling a .aspx web page through the client and then redirecting it to perform the CRUD operations. But this is not what REST is about. We have to expose the object Employee to the calling client and not the .aspx web page. How can this be achieved? Enter HTTP handlers!!

We would have to configure IIS to handle the employee web request. Let us create an HTTP handler for this. Refer the Service class in the RestWebService project. The class implements the IHTTPHandler interface.

public class Service:IHttpHandler

This interface implements the following two methods:

  • bool IHttpHandler.IsReusable

    This property is called to determine whether this instance of the HTTP handler can be reused for fulfilling other requests of the same type. HTTP handlers can return either true or false in order to specify whether they can be reused.

  • void IHttpHandler.ProcessRequest(HttpContext context)

    For the time being, we will concentrate on the ProcessRequest() method. This is the method which gets invoked when IIS invokes the ISAPI filter after receiving the client request. This can be considered as the entry point of any application using HTTP handlers. The crux of the code lies here.

The first thing that we have to do is decide what kind of request has come to us and act accordingly. We will do the following:

  1. GET method – READ database for employee information
  2. PUT method – UPDATE database
  3. POST method – INSERT database
  4. DELETE method – DELETE database

The HTTPContext class has Request and Response objects. The code below uses them:

//Handling CRUD
switch (context.Request.HttpMethod)
{
case "GET":
//Perform READ Operation
READ(context);
break;
case "POST":
//Perform CREATE Operation
CREATE(context);
break;
case "PUT":
//Perform UPDATE Operation
UPDATE(context);
break;
case "DELETE":
//Perform DELETE Operation
DELETE(context);
break;
default:
break;
}

READ() method

  1. Get the employee code from the query string of the URL.
  2. Poll the database for that particular employee using the Data Access Layer discussed above.
  3. Serialize the Employee class into XML and write to the Response object.
int employeeCode = Convert.ToInt16(context.Request["id"]);

emp = dal.GetEmployee(employeeCode);
if (emp==null)
context.Response.Write(employeeCode + "No Employee Found");

string serializedEmployee = Serialize(emp);
context.Response.ContentType = "text/xml";
WriteResponse(serializedEmployee);

CREATE() method

  1. Extract the Employee class from the message body of the POST request. This is done by using the BinaryRead() method of the Request class which reads the message body as bytes.
  2. Deserialize employee information from bytes[] to the Employee class.
  3. Perform an Insert operation in the database using the Data Access Layer.
// Extract the content of the Request and make a employee class
// The message body is posted as bytes. read the bytes
byte[] PostData = context.Request.BinaryRead(context.Request.ContentLength);
//Convert the bytes to string using Encoding class
string str = Encoding.UTF8.GetString(PostData);
// deserialize xml into employee class
Company.Employee emp = Deserialize(PostData);
// Insert data in database
dal.AddEmployee(emp);

UPDATE() method

  1. Extract the Employee class from the message body of the PUT request. This is done by using the BinaryRead() method of the Request class which reads the message body as bytes.
  2. Deserialize employee information from bytes[] to the Employee class.
  3. Perform the Update operation in the database using the Data Access Layer.
byte[] PUTRequestByte = context.Request.BinaryRead(context.Request.ContentLength);
context.Response.Write(PUTRequestByte);

// Deserialize Employee
Company.Employee emp = Deserialize(PUTRequestByte);
dal.UpdateEmployee(emp);

DELETE() method

  1. Get the employee code from the query string of the URL.
  2. Perform the Delete operation in the database.
int EmpCode = Convert.ToInt16(context.Request["id"]);
dal.DeleteEmployee(EmpCode);
WriteResponse("Employee Deleted Successfully");

The Service class has association with the DAL class to handle database operations. I have the connection string stored in the project properties, and access it through the following line of code:

connString = Properties.Settings.Default.ConnectionString;

Also, I will briefly explain the methods responsible for XML serialization and deserialization.

The Serialize() method takes care of XML serialization. It uses the XmlSerializer class that uses the Serialize() method to write the XML into a memory stream. It then returns the XML Employee in a string using the ToArray() method of the memory stream. Refer the code below:

private String Serialize(Company.Employee emp)
{
try
{
String XmlizedString = null;
XmlSerializer xs = new XmlSerializer(typeof(Company.Employee));
//create an instance of the MemoryStream class since we intend to keep the XML string
//in memory instead of saving it to a file.
MemoryStream memoryStream = new MemoryStream();
//XmlTextWriter - fast, non-cached, forward-only way of generating streams or files
//containing XML data
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
//Serialize emp in the xmlTextWriter
xs.Serialize(xmlTextWriter, emp);
//Get the BaseStream of the xmlTextWriter in the Memory Stream
memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
//Convert to array
XmlizedString = UTF8ByteArrayToString(memoryStream.ToArray());
return XmlizedString;
}
catch (Exception ex)
{
errHandler.ErrorMessage = ex.Message.ToString();
throw;
}
}

The Deserialize() method takes care of the deserialization. It accepts employee information as a byte array, uses the XmlSerializer class to deserialize it, and returns an Employee object.

private Company.Employee Deserialize(byte[] xmlByteData)
{
try
{
XmlSerializer ds = new XmlSerializer(typeof(Company.Employee));
MemoryStream memoryStream = new MemoryStream(xmlByteData);
Company.Employee emp = new Company.Employee();
emp = (Company.Employee)ds.Deserialize(memoryStream);
return emp;
}
catch (Exception ex)
{
errHandler.ErrorMessage = dal.GetException();
errHandler.ErrorMessage = ex.Message.ToString();
throw;
}
}

Exception handling

Handling and detecting errors can be a challenge as everything in the Web Service will happen behind the scenes. There is no way to debug the code using break points. I have used a project library called ErrorHandler which has a class called ErrorHandler:

public class ErrorHandler
{
static StringBuilder errMessage = new StringBuilder();

static ErrorHandler()
{
}
public string ErrorMessage
{
get {return errMessage.ToString();}
set
{
errMessage.AppendLine(value);
}
}
}

It has a public property which stores error messages and returns when called upon. Whenever an exception occurs, the error message property is set to the exception message. Being a StringBuilder, it can store error messages at various levels. Finally, this can be called at the final level to get all the error messages encountered in the code.

Another way of doing it will be to use Trace and Listeners. You can trace the flow of the code and log it in an XML or text file.

catch (Exception ex)
{
errHandler.ErrorMessage = dal.GetException();
errHandler.ErrorMessage = ex.Message.ToString();
}

Part # 5 – Deploying the application

Now that the code is in place, let’s talk about deploying the application. I have used IIS 7.5 in Windows 7. If you are using other versions, please make adjustments accordingly.

  1. Create a virtual directory in the root directory of your Default Web site. Convert this into a web application. In IIS 7.5, right click on the virtual directory in inetmgr and select “Convert To Application”.

    Example: C:\inetpub\wwwroot\RestWebService.

  2. Create a Bin folder inside your virtual directory so that it looks like this: C:\inetPub\wwwroot\RestWebService\Bin.
  3. Compile your application and place the DLLs in the Bin folder. You should have three DLLs:

    1. Company.dll – Employee class
    2. DAL.dll – Data Access Layer
    3. RestWebService.dll – REST Web Service

  4. Place a Web.config file in the root directory of your Web site, i.e., in the RestWebService folder:
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
    <system.web>
    <httpHandlers>
    <add type="RestWebService.Service, RestWebService" verb="*" path="employee" />
    </httpHandlers>
    </system.web>
    <system.webServer>
    <handlers>
    <add name="employee" path="employee" verb="*" modules="IsapiModule"
    scriptProcessor="C:\Windows\Microsoft.NET\Framework\
    v2.0.50727\aspnet_isapi.dll"

    resourceType="Unspecified" requireAccess="Script"
    preCondition="classicMode,runtimeVersionv2.0,bitness32" />
    </handlers>
    <directoryBrowse enabled="false" />
    </system.webServer>
    </configuration>

    In the httpHandler section:

    1. type represents the fully qualified class name – RestWebService.Service and the assembly name RestWebService.dll – Namespace.Class, Assembly.
    2. verb represents the HTTP verbs allowed on the service. * specifies all verbs are allowed.
    3. path – This is where we are exposing our employee object. The path specifies the call that IIS is going to handle for this application. In other words, any request with employee will be directed to our RestWebService.dll.

    In the handlers secttion, we are mapping a DLL (aspnet_isapi.dll) which will do the above work.

    This can be done through inetmgr also, as follows:

    1. In inetmgr, select your directory and double click on Handler Mapping.
    2. Click “Add Script Map” and do the following:

      image002

    3. You should have the HTTP Handler mapped:

      image003

  5. To test whether everything works fine, insert some data in the database and type the following in your web browser: https://localhost/RestWebService/employee?id=3550. If you have an employee with code = 3550, you will see the following:

    image004

    Note: For issues related to deployment, please see the References section at the end of the article.

Part # 6 – Testing the application

The GET method can be tested through the browser, but to test the other HTTP methods, we have to write our own code. I have created a console application called TestHarness for this.

Note that this application also performs testing for various parts of the project. I have written code to test the database operations to check that my Data Access Layer works fine.

I will explain the testing of the GET, POST, PUT, and DELETE methods below:

  1. GenerateGetRequest() – Testing the GET method.

    1. Use HTTPWEbRequest.Create(Url) to create an instance of HTTPWebRequest.
    2. Set the Method property to GE,T and use the GetResponse() method to receive the response stream from the server.
    3. Print the result in the console to check the result:
      string url = "https://localhost/RestWebService/employee?id=3550";
      HttpWebRequest GETRequest = (HttpWebRequest)WebRequest.Create(url);
      GETRequest.Method = "GET";

      Console.WriteLine("Sending GET Request");
      HttpWebResponse GETResponse = (HttpWebResponse)GETRequest.GetResponse();
      Stream GETResponseStream = GETResponse.GetResponseStream();
      StreamReader sr = new StreamReader(GETResponseStream);

      Console.WriteLine("Response from Server");
      Console.WriteLine(sr.ReadToEnd());

  2. GeneratePOSTRequest() – Testing the POST method.

    1. Create the message body. The message body should contain the Employee class XML serialized. I am using a method GenerateXMLEmployee() which creates a stream of bytes containing the employee information. I am using XmlTextWriter to create the XML in a memory stream and return a byte array from that stream.
      private static byte[] GenerateXMLEmployee(string strFirstName, 
      string strLastName, int intEmpCode, string strDesignation)
      {
      // Create the xml document in a memory stream - Recommended
      MemoryStream mStream = new MemoryStream();
      //XmlTextWriter xmlWriter =
      // new XmlTextWriter(@"C:\Employee.xml", Encoding.UTF8);
      XmlTextWriter xmlWriter = new XmlTextWriter(mStream, Encoding.UTF8);
      xmlWriter.Formatting = Formatting.Indented;
      xmlWriter.WriteStartDocument();
      xmlWriter.WriteStartElement("Employee");
      xmlWriter.WriteStartElement("FirstName");
      xmlWriter.WriteString(strFirstName);
      xmlWriter.WriteEndElement();
      xmlWriter.WriteStartElement("LastName");
      xmlWriter.WriteString(strLastName);
      xmlWriter.WriteEndElement();
      xmlWriter.WriteStartElement("EmpCode");
      xmlWriter.WriteValue(intEmpCode);
      xmlWriter.WriteEndElement();
      xmlWriter.WriteStartElement("Designation");
      xmlWriter.WriteString(strDesignation);
      xmlWriter.WriteEndElement();
      xmlWriter.WriteEndElement();
      xmlWriter.WriteEndDocument();
      xmlWriter.Flush();
      xmlWriter.Close();
      return mStream.ToArray();
      }

    2. Once the message body is created (employee information in byte array), we can create the POST request in the same way. The only addition is that we have to write the data bytes in the stream of the request object and get the server response. See the code below:
      string strURL = "https://localhost/RestWebService/employee";
      byte[] dataByte =
      GenerateXMLEmployee(strFirstName,strLastName,EmpCode,strDesignation);

      HttpWebRequest POSTRequest = (HttpWebRequest)WebRequest.Create(strURL);
      //Method type
      POSTRequest.Method = "POST";
      // Data type - message body coming in xml
      POSTRequest.ContentType = "text/xml";
      POSTRequest.KeepAlive = false;
      POSTRequest.Timeout = 5000;
      //Content length of message body
      POSTRequest.ContentLength = dataByte.Length;

      // Get the request stream
      Stream POSTstream = POSTRequest.GetRequestStream();
      // Write the data bytes in the request stream
      POSTstream.Write(dataByte, 0, dataByte.Length);

      //Get response from server
      HttpWebResponse POSTResponse = (HttpWebResponse)POSTRequest.GetResponse();
      StreamReader reader =
      new StreamReader(POSTResponse.GetResponseStream(),Encoding.UTF8) ;
      Console.WriteLine("Response");
      Console.WriteLine(reader.ReadToEnd().ToString());

  3. GeneratePUTRequest() – Testing the PUT method.

    This is the same as the POST request. The employee information sent as XML here is an existing employee rather than a new one.

  4. GenerateDELETERequest() – Testing the Delete method.

    This is the same as the POST method as explained above.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.