Friday, July 24, 2015

Step By Step : Create and Consume WCF Restful Service

Hi,

Welcome to my blog. In this post we will see how we can create a WCF RESTFUL service and how Windows Forms Application calls methods in that WCF RESTFUL service using WebRequest and WebClient classes.

Overview:
When I started to look into Restful web services, I was able to find many posts regarding how to implement a simple RESTful web service using WCF. But it was hard for me to find out how to calling RESTFUL service methods using HTTP verbs like POST, GET, DELETE and PUSH using C# code. Every post I came across was happpy to tell about call GET methods using Fiddler or browser or call methods using Javascript ot JQuery. I wanted some sample using C# code which calls all methods using HTTP verbs like POST, GET, DELETE and PUSH. So, I wrote this post so that it will be helpful for the needy.

In this post we will see step by step process of how to implement a simple RESTful web service using WCF and using a Windows Forms application and call WCF RESTFUL service methods using HTTP verbs like POST, GET, DELETE and PUSH.

This is just one of the way which I followed  I am not saying this is the best and it doesnot have cons. My main aim is to share my experience so that it will be useful for the needy. I tried to make this post as much interesting as possible. Please excuse me for my grammatical errors and typos. I welcome your constructive comments and suggestions which helps in improving this post and my technical skills.

Introduction:
REST stands for Representational State Transfer. RESTful web services use HTTP verbs. They expose either a collection resource (representational of a list) or an element resource (representational of a single item in the list).

HTTP verbs maps CRUD operations to HTTP methods as below

Create (POST) => create a new resource.
Read (GET) => retrieve one or many resources.
Update (PUT) => update an existing resource.
Delete (DELETE) => delete an existing resource.

Now let's start our work
For this article, I am using the following tools/technologies to develop a my sample application
Visual Studio 2013
C#
.NET Framework 4.5

Our sample application will launch a windows form UI with a datagrid view to show results and it has buttons to do the following
Get Books : Get all books. This uses WebRequest class to make service call.
Get Book : Get a book depending on ID. This uses WebClient class to make service call.
Add Book : Create a new book.This uses WebRequest class to make service call
Update Book: Update an existing book with new details. This uses WebClient class to make service call
Delete Book: Delete a particular book. This uses WebRequest class to make service call

The final UI will look something like this.























I always have the habit of creating empty solution first and then work on it. Here also I did same thing. I created an empty visual studio solution titled "RestfulTutorials" and added below 3 projects.
Data : This a standard C# class library. The data project will contain our entities, and all logic that provided data to our application. In this case, for simplicity, I added nothing.

WCFService: This is a WCF Service library and contains our WCF RESTful service definition, all associated configuration, and each of our CRUD methods

WindowsClient: This is a sample windows forms application that will call WCF service methods using HTTP verbs(POST,PUT...).

After adding your projects your solution must look like below.
























Data : Add new class and name it as Book.cs. Add below code to it. We need to decorated Book class with DataContract attribute so that it can be passed between client ( WindowsClient) and service (WCFService). Book class is serialized and pass between client and server. Each property that we want to be serialized is decorated with DataMember attribute

[DataContract]
    public class Book
    {
        [DataMember]
        public int Id { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public float Price { get; set; }
    }


Data project is simple. As I said it has nothing. Build your solution to make sure you donot have any errors.

WCFService: Make sure you have below references in your WCFService project and rename IService1.cs to IBookService.cs and Service1.cs to BookService.cs. Delete everything in IBookService.cs and BookService.cs. You project must look like below



















Build your solution to make sure you donot have any errors.
Open App.config and do the following

1) Make sure that the service name matches the full namespace for your service interface;
<system.serviceModel>
    <services>
      <service name="WCFService.BookService">
    
2) Add behavior like below
<endpointBehaviors>
        <behavior name="web">
          <webHttp automaticFormatSelectionEnabled="true" defaultOutgoingResponseFormat="Json"/>
        </behavior>
</endpointBehaviors>

3) Add endpoint with webHttpBinding like below and with behaviorConfiguration equals to above behavior name.
 <endpoint address="" binding="webHttpBinding" contract="WCFService.IBookService" behaviorConfiguration="web">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>

4) Update the baseAddress appropriately. Here I am updating the base address to tell WCF to use the port number 8085 and simplifed the address
<host>
          <baseAddresses>
            <add baseAddress="http://localhost:8085/BookService/" />
          </baseAddresses>
 </host>

Your App.config must looks like below






















Now add a reference to the Data project to WCFService project since we need to use Book object in service project.

Now in IBookService.cs add GetBooks() like below which returns list of Books. The WebGet attribute is a REST specific attribute indicating that the operation is accessible via the HTTP GET verb.  The WebInvoke attribute can be used for POST, PUT and DELETE verbs. To make the operations RESTful, we need to update WebGet attribute to indicate that we can execute this method using http://localhost:8085/BookService/Books url.
 [OperationContract]
 [WebGet(UriTemplate="/Books")]
 IList<Book> GetBooks();



It is also possible to pass parameters into URL in order to return specific book rather than all books. Please add GetBook method like below.
  [OperationContract]
  [WebGet(UriTemplate = "/Book/{id}")]
  Book GetBookById(string id);
       
A place-marker ({id}) is used to indicate that a parameter will be provided, and that marker matches the name of the parameter accepted by the operation.  Unfortunately, parameter type in URL as always strings. We have to manually cast the string to desired type before using it.

Other HTTP verbs are just as easy to implement.  Add the following operations to your service contract

 [OperationContract]
 [WebInvoke(Method="POST", UriTemplate="/Book")]
  IList<Book> CreateBook(Book newBook);

  [OperationContract]
  [WebInvoke(Method="PUT", UriTemplate="/Book")]
  IList<Book> UpdateBook(Book book);

   [OperationContract]
   [WebInvoke(Method="DELETE", UriTemplate="/Book/{id}")]
   IList<Book> DeleteBook(string id);

Instead of using the WebGet attribute, we use the WebInvoke attribute, which basically means most other verbs other than GET.  As we’re not using Uri templates with place-markers, we can pass in a complex object from the client and WCF will just figure out what to do with it.

For completeness, here are IBookService.cs and BookService.cs;

public interface IBookService
    {
        // TODO: Add your service operations here
        [OperationContract]
        [WebGet(UriTemplate="/Books")]
        IList<Book> GetBooks();

        [OperationContract]
        [WebGet(UriTemplate = "/Book/{id}")]
        Book GetBookById(string id);

        [OperationContract]
        [WebInvoke(Method="POST", UriTemplate="/Book")]
        IList<Book> CreateBook(Book newBook);

        [OperationContract]
        [WebInvoke(Method="PUT", UriTemplate="/Book")]
        IList<Book> UpdateBook(Book book);

        [OperationContract]
        [WebInvoke(Method="DELETE", UriTemplate="/Book/{id}")]
        IList<Book> DeleteBook(string id);
    }  

 public class BookService : IBookService
    {
        IList<Book> books = null;
        public BookService()
        {
            books = new List<Book>();

            books.Add(new Book() { Id = 1, Name = "Book 1", Price = 10.34f });
            books.Add(new Book() { Id = 2, Name = "Book 2", Price = 20.34f });
            books.Add(new Book() { Id = 3, Name = "Book 3", Price = 30.34f });
            books.Add(new Book() { Id = 4, Name = "Book 4", Price = 40.34f });
            books.Add(new Book() { Id = 5, Name = "Book 5", Price = 50.34f });
        }
        public IList<Book> GetBooks()
        {
            return books;
        }

        public Book GetBookById(string id)
        {
            int bookId = Convert.ToInt32(id);
            return books.Where(book => book.Id == bookId).FirstOrDefault();
        }

        public IList<Book> CreateBook(Book newBook)
        {
            int newBookId = books.Max(x => x.Id);
            books.Add(new Book() {Id = newBookId+1, Name=newBook.Name, Price = newBook.Price });
            return books;
        }

        public IList<Book> UpdateBook(Book book)
        {
            Book updateBook = books.Where(x => x.Id == book.Id).First();
            updateBook.Name = book.Name;
            updateBook.Price = book.Price;
            return books;
        }

        public IList<Book> DeleteBook(string id)
        {
            Book deleteBook = books.Where(x => x.Id == Convert.ToInt32(id)).First();
            books.Remove(deleteBook);
            return books;
        }
    }

Build your solution to make sure you donot have any errors.

WindowsClient : Now add a reference to the Data project to WindowsClient project since we need to use Book object in service project.. Create windows form UI with 5 buttons and a datagridview.Here we demonstrate how to work with both WebRequest and WebClient classes.

WebRequest : This is an abstract class and we cannot use it directly. We use Create method to create an instance of WebRequest and use GetResponseStream() to return a data stream. FileWebRequest and FtpWebRequest classes inherit from WebRequest. We use WebRequest to make a request and convert the return to either HttpWebRequest, FileWebRequest or FtpWebRequest, depending on our request. Using HttpWebRequest, we have to get the response of our request, instantiate StreamReader to read the response and finally, convert the result to whatever type we expect

WebClient: It provides common operations to sending and receiving data from a resource identified by a URI. It is a higher-level abstraction of HttpWebRequest. The operations like DownloadData and DownloadFile is what differentiate WebClient from HttpWebRequest and simplify code we would do with HttpWebRequest.

The simplest one is calling GET methods. Please see below for GetBooks and GetBook methods calling.

GetBooks: Using WebRequest
private void btnGetBooks_Click(object sender, EventArgs e)
        {
            try
            {
                //This will match GetBooks in IBookService.cs
                WebRequest request = WebRequest.Create(@"http://localhost:8085/BookService/Books");
                request.Method = "GET"; //HTTP : GET method

                using (WebResponse response = request.GetResponse()) // calling REST method
                {
                    //below code to access the result.
                    using (Stream responseStream = response.GetResponseStream())
                    {
                        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(IList<Book>));
                        IList<Book> books = (IList<Book>)serializer.ReadObject(responseStream);

                        bindingSource1.DataSource = books;
                        dvBooks.DataSource = bindingSource1;
                    }
                }
            }
            catch (Exception ex)
            {

            }

        }


GetBook : Using WebClient
   private void btnGetBook_Click(object sender, EventArgs e)
        {
            try
            {
               using(WebClient client = new WebClient())
               {
                   Uri uri = new Uri(@"http://localhost:8085/BookService/Book/2");
                   client.DownloadDataCompleted += OnGetBookCompleted;
                   client.DownloadDataAsync(uri);
               }

            }
            catch (Exception ex)
            {

            }
        }

        void OnGetBookCompleted(object sender, DownloadDataCompletedEventArgs e)
        {
            byte[] result = e.Result as byte[];
            MemoryStream responseStream = new MemoryStream(result);
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Book));
            Book book = (Book)serializer.ReadObject(responseStream);

            bindingSource1.DataSource = book;
            dvBooks.DataSource = bindingSource1;

        }


DeleteBook : This is also simple. Using WebRequest
 private void btnDeleteBook_Click(object sender, EventArgs e)
        {
            try
            {
               //This will match DeleteBook in IBookService.cs. I am hard coding id here
                WebRequest request = WebRequest.Create(@"http://localhost:8085/BookService/Book/2");
                request.Method = "DELETE"; // HTTP Delete Method

                //invoke REST method
                using (WebResponse response = request.GetResponse())
                {
                    //below code to access the result.
                    using (Stream responseStream = response.GetResponseStream())
                    {
                        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(IList<Book>));
                        IList<Book> books = (IList<Book>)serializer.ReadObject(responseStream);

                        bindingSource1.DataSource = books;
                        dvBooks.DataSource = bindingSource1;
                    }
                }
            }
            catch (Exception ex)
            {

            }
        }

AddBook: This is little bit tricky since we need to provide new book that must be added to collection. Please see below code. Using WebRequest
 private void btnAddBook_Click(object sender, EventArgs e)
        {
            try
            {
                Book newBook = new Book() { Id = 0, Name = "New Book", Price = 123.45f };

                //create new request
                //This will match AddBook in IBookService.cs
                WebRequest request = WebRequest.Create(@"http://localhost:8085/BookService/Book");
                request.Method = "POST";
                request.ContentType = "application/json; charset=utf-8";

                //add new update book info to request
                Stream stream = request.GetRequestStream();
                DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Book));
                ser.WriteObject(stream, newBook);

                //invoke REST method
                using (WebResponse response = request.GetResponse())
                {
                    using (Stream responseStream = response.GetResponseStream())
                    {
                        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(IList<Book>));
                        IList<Book> books = (IList<Book>)serializer.ReadObject(responseStream); // since we get collection back

                        bindingSource1.DataSource = books;
                        dvBooks.DataSource = bindingSource1;
                    }
                }
            }
            catch (Exception ex)
            {

            }
        }

UpdateBook: This is little bit tricky since we need to provide new book that must be updated and added to collection. Please see below code. Using WebClient
 private void btnUpdateBook_Click(object sender, EventArgs e)
        {
            try
            {
                using(WebClient client = new WebClient())
                {
                    client.Headers[HttpRequestHeader.ContentType] = "text/json";                  
                    Uri uri = new Uri(@"http://localhost:8085/BookService/Book");

                    Book updateBook = new Book() { Id = 3, Name = "UpdateBook Name 3", Price = 77.77f };

                    MemoryStream requestStream = new MemoryStream();
                    DataContractJsonSerializer requestSerializer = new DataContractJsonSerializer(typeof(Book));
                    requestSerializer.WriteObject(requestStream, updateBook);

                    client.UploadDataCompleted += OnUpdateBookCompleted;
                    client.UploadDataAsync(uri, "PUT",requestStream.ToArray());
                }

            }
            catch (Exception ex)
            {

            }
        }

        void OnUpdateBookCompleted(object sender, UploadDataCompletedEventArgs e)
        {
            byte[] result = e.Result as byte[];
            MemoryStream responseStream = new MemoryStream(result);
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(IList<Book>));
            IList<Book> books = (IList<Book>)serializer.ReadObject(responseStream);

            bindingSource1.DataSource = books;
            dvBooks.DataSource = bindingSource1;

        }


For completeness, here is full code for Form1.cs in WindowsClient project


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Net;
using System.IO;
using System.Xml.XPath;
using System.Xml;
using Data;
using System.Runtime.Serialization.Json;

namespace WindowsClient
{
    public partial class Form1 : Form
    {
        private BindingSource bindingSource1 = new BindingSource();

        public Form1()
        {
            InitializeComponent();
        }

        //GetBooks : GET
        private void btnGetBooks_Click(object sender, EventArgs e)
        {
            try
            {
                WebRequest request = WebRequest.Create(@"http://localhost:8085/BookService/Books");
                request.Method = "GET";

                using (WebResponse response = request.GetResponse())
                {
                    using (Stream responseStream = response.GetResponseStream())
                    {
                        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(IList<Book>));
                        IList<Book> books = (IList<Book>)serializer.ReadObject(responseStream);

                        bindingSource1.DataSource = books;
                        dvBooks.DataSource = bindingSource1;
                    }
                }
            }
            catch (Exception ex)
            {

            }

        }

        //GetBook : GET
        private void btnGetBook_Click(object sender, EventArgs e)
        {
            try
            {
               using(WebClient client = new WebClient())
               {
                   Uri uri = new Uri(@"http://localhost:8085/BookService/Book/2");
                   client.DownloadDataCompleted += OnGetBookCompleted;
                   client.DownloadDataAsync(uri);
               }
            }
            catch (Exception ex)
            {

            }
        }

        void OnGetBookCompleted(object sender, DownloadDataCompletedEventArgs e)
        {
            byte[] result = e.Result as byte[];
            MemoryStream responseStream = new MemoryStream(result);
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Book));
            Book book = (Book)serializer.ReadObject(responseStream);

            bindingSource1.DataSource = book;
            dvBooks.DataSource = bindingSource1;
        }

        private void btnUpdateBook_Click(object sender, EventArgs e)
        {
            try
            {
                using(WebClient client = new WebClient())
                {
                    client.Headers[HttpRequestHeader.ContentType] = "text/json";                  
                    Uri uri = new Uri(@"http://localhost:8085/BookService/Book");

                    Book updateBook = new Book() { Id = 3, Name = "UpdateBook Name 3", Price = 77.77f };

                    MemoryStream requestStream = new MemoryStream();
                    DataContractJsonSerializer requestSerializer = new DataContractJsonSerializer(typeof(Book));
                    requestSerializer.WriteObject(requestStream, updateBook);

                    client.UploadDataCompleted += OnUpdateBookCompleted;
                    client.UploadDataAsync(uri, "PUT",requestStream.ToArray());
                }
            }
            catch (Exception ex)
            {

            }
        }

        void OnUpdateBookCompleted(object sender, UploadDataCompletedEventArgs e)
        {
            byte[] result = e.Result as byte[];
            MemoryStream responseStream = new MemoryStream(result);
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(IList<Book>));
            IList<Book> books = (IList<Book>)serializer.ReadObject(responseStream);

            bindingSource1.DataSource = books;
            dvBooks.DataSource = bindingSource1;
        }
      
        private void btnAddBook_Click(object sender, EventArgs e)
        {
            try
            {
                Book newBook = new Book() { Id = 0, Name = "New Book", Price = 123.45f };

                //create new request
                WebRequest request = WebRequest.Create(@"http://localhost:8085/BookService/Book");
                request.Method = "POST";
                request.ContentType = "application/json; charset=utf-8";

                //add new update book info to request
                Stream stream = request.GetRequestStream();
                DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Book));
                ser.WriteObject(stream, newBook);


                //invoke REST method
                using (WebResponse response = request.GetResponse())
                {
                    using (Stream responseStream = response.GetResponseStream())
                    {
                        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(IList<Book>));
                        IList<Book> books = (IList<Book>)serializer.ReadObject(responseStream);

                        bindingSource1.DataSource = books;
                        dvBooks.DataSource = bindingSource1;
                    }
                }
            }
            catch (Exception ex)
            {

            }
        }

        private void btnDeleteBook_Click(object sender, EventArgs e)
        {
            try
            {
                WebRequest request = WebRequest.Create(@"http://localhost:8085/BookService/Book/2");
                request.Method = "DELETE";

                //invoke REST method
                using (WebResponse response = request.GetResponse())
                {
                    using (Stream responseStream = response.GetResponseStream())
                    {
                        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(IList<Book>));
                        IList<Book> books = (IList<Book>)serializer.ReadObject(responseStream);

                        bindingSource1.DataSource = books;
                        dvBooks.DataSource = bindingSource1;
                    }
                }
            }
            catch (Exception ex)
            {

            }
        }
    }

}



That's it. Build your application so that we donot have any errors.
The easiest way to run your application is to do following. Right click on your Solution and go to Properties and select Multiple startup projects and Start WCFSErvice and WindowsClient projects as show below and click Ok.























Now run your application by simply pressing F5 which starts your WCFService and WindowsClient projects and if everything goes fine all the buttons must work like below.

GetBooks:


GetBook:


DeleteBook:























AddBook:























UpdateBook:
























Conclusion:
In this article we learned how to implement a simple RESTful web service using WCF and how we can call all the Restful HTTP methods using HTTP verbs like PUT, POST, GET, DELETE by using a simple windows form client application.

Please feel free to let me know your valuable and constructive comments and suggestions which can help me to make this article better and improve my technical and written skills. Last but not the least please excuse me for my grammar and typos.

Thanks and have a nice and wonderful day.

No comments:

Post a Comment

Please add your comments. Please avoid personal attacks and racist comments. Your constructive comments are always welcome. I will try my best to address your comments in time.