The term Representational State Transfer (REST) was coined by Roy Fielding and is a design pattern for a networked system. In this pattern, the client invokes a web request using a URL, so the basic idea of a REST pattern is to generate a unique URI to represent data.
In Wikipedia, REST is quoted as:
"Representational State Transfer is intended to evoke an image of how a well-designed Web application behaves: a network of web pages (a virtual state-machine), where the user progresses through an application by selecting links (state transitions), resulting in the next page (representing the next state of the application) being transferred to the user and rendered for their use."
This means REST is intended for web applications where the browser invokes each request by specifying a unique URI, as shown in the following figure:
In the preceding figure, it is clear that any technology can easily call a REST-based service using basic HTTP requests.
There are a few basic thoughts behind the implementation of REST-based services. Let's look into them:
REST uses web verbs to communicate to services. Verbs such as GET
, POST
, DELETE
, and PUT
are major verbs for REST-based applications. If you consider Hi-REST for your application, you should be strict in choosing these verbs. The norms are as follows:
REST services have a number of major advantages, which people are always inclined toward in order to implement:
The features of REST-based services are never-ending. It is very popular nowadays not only because it is interoperable, but because it is easily consumed by the existing toolsets that support standard HTTP verbs.
Let's now create a REST-based WCF service using the following steps:
ContactService
.IService1.cs
and Service1.svc
), and add a new WCF Service to the solution. We will call it ContactCRUD.svc
.IContactCRUD
interface:[ServiceContract] public interface IContactCRUD { [WebGet(UriTemplate = "contact/{roll}")] [OperationContract] Contact GetContact(string roll); [WebInvoke(Method = "POST", UriTemplate = "contacts")] [OperationContract] bool SaveContact(Contact currentContact); [WebInvoke(Method = "DELETE", UriTemplate = "contact/{roll}")] [OperationContract] bool RemoveContact(string roll); [WebInvoke(Method = "PUT", UriTemplate = "contact")] [OperationContract] bool UpdateContacts(Contact contact); [WebGet(UriTemplate = "contacts")] [OperationContract] List<Contact> GetAllContacts(); } [DataContract] public class Contact { [DataMember] public int Roll { get; set; } [DataMember] public string Name { get; set; } [DataMember] public string Address { get; set; } [DataMember] public int Age { get; set; } }
In the preceding code, we have specified the WebInvoke
method to define the REST configuration for each interface. The WebGet
method is used only for GET
requests, while you can employ other methods easily using WebInvoke
. Each of the attributes takes UriTemplate
, which defines the routing URL that points to the service.
It is also worth mentioning that you can specify the request/response format for a particular interface as well. If you do not specify anything, it will take XML as a medium of transport. You can also specify JSON on either WebInvoke
or WebGet
as follows:
[WebGet(UriTemplate = "contacts", RequestFormat= WebMessageFormat.Json, ResponseFormat= WebMessageFormat.Json)]
GetContact
method will get an individual contact object from the list, the SaveContact
method will put the contact on the list, the DeleteContact
method will remove that particular contact from the list, and the UpdateContact
method will update a contact element.webHttpBinding
as the binding. Let's consider configuring the service as shown in the following code:<system.serviceModel> <services> <service name="ContactService.ContactCRUD" behaviorConfiguration="httpBehavior"> <endpoint address="" binding="webHttpBinding" contract="ContactService.IContactCRUD" behaviorConfiguration="httpEndpointBehavior"> <identity> <dns value="localhost"/> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> <behaviors> <serviceBehaviors> <behavior name="httpBehavior"> <serviceMetadata httpGetEnabled="True"/> <serviceDebug includeExceptionDetailInFaults="False"/> </behavior> </serviceBehaviors> <endpointBehaviors> <behavior name="httpEndpointBehavior"> <webHttp /> </behavior> </endpointBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel>
Here, the service is configured with webHttpBinding
and a contract pointing to ContactService.IContactCrud
. The service is getting hosted at port 8080
. Remember to specify httpGetEnabled=true
to ensure that the application can invoke the GET request, which is required for REST-based services.
Testpage
on the project to invoke the requests. We are going to use Jquery ajax
to request the service.JS
folder and add a few buttons to handle each of these service interfaces. Our service is hosted as ContactCRUD.svc
in IIS and we can call this URL to communicate with the service. Let's create a small UI with one table and a few textboxes to take values for the contact class. We created a few buttons and a table on the screen to show the data from the service, as shown in the following screenshot:In the previous UI that we built, we placed a number of buttons, each of which pointed to a certain web service interface. We are going to call each service through these buttons and update the server. For instance, GetContacts
will call GetService
to get all contacts from the server, while UpdateContact
calls the PUT service to update the existing contact information on the server.
GET
service for GetContact
and GetContact
(by roll), as shown in the following code:function returnContacts() { $.ajax({ type: "GET", url: "ContactCRUD.svc/Contacts", success: function (data) { updateTable(data); }, error: function (msg) { alert(msg); } }); } function returnContact() { var roll = $("#roll").val(); $.ajax({ type: "GET", url: "ContactCRUD.svc/contact/" + roll, success: function (data) { updateTable(data); }, error: function (msg) { alert(msg); } }); } function updateTable(data) { $("#output").find("tr:gt(0)").remove(); $(data).find('Contact').each(function () { var roll = $(this).find('Roll').text(); var name = $(this).find('Name').text(); var age = $(this).find('Age').text(); var address = $(this).find('Address').text(); $('<tr></tr>').html('<th>' + roll + '</th><td>' + name + '</td><td>' + age + '</td><td>' + address + '</td>').appendTo('#output'), }); }
In the preceding code, we made a GET
request to the server. The first one is the request for all contacts without any parameters while the second one is for contacts with parameters. The $.ajax
function is an API to call the server side. The AJAX requests receive an XML response, which is then appended to the table with the ID output using the updateTable
function.
POST
request, the data needs to be sent inside the body of the AJAX object. jQuery provides a data property, which can be used to set the data that needs to be posted to the service:function postContact() { var postObj = "<Contact><Address>" + $("#address").val() + "</Address><Age>" + $("#age").val() + "</Age><Name>" + $("#name").val() + "</Name><Roll>" + $("#roll").val() + "</Roll></Contact>"; $.ajax({ type: "POST", url: "ContactCRUD.svc/contacts", contentType: "application/xml; charset=utf-8", data: postObj, success: function (data) { returnContacts(); $(":text").val(''), }, error: function (msg) { alert(msg); } }); }
In the preceding code, we are posting an XML contact element to the service. The contentType
method here is essential to let the service know that the data that sent is actually XML content.
POST
, the PUT
request also sends data inside the body of an AJAX call. The PUT
request is used to update data on the server:function putContact() { var postObj = "<Contact><Address>" + $("#address").val() + "</Address><Age>" + $("#age").val() + "</Age><Name>" + $("#name").val() + "</Name><Roll>" + $("#roll").val() + "</Roll></Contact>"; $.ajax({ type: "PUT", url: "ContactCRUD.svc/contact", contentType: "application/xml; charset=utf-8", data: postObj, success: function (data) { returnContacts(); }, error: function (msg) { alert(msg); } }); }
Similar to the POST
request, the PUT
request also posts data into the body, while the type specified as PUT
and the service URL are changed.
DELETE
statement is used to delete content from the server:function deleteContact() { var roll = $("#roll").val(); $.ajax({ type: "DELETE", url: "ContactCRUD.svc/contact/" + roll, success: function (data) { returnContacts(); }, error: function (msg) { alert(msg); } }); }
The preceding AJAX call invokes a DELETE
statement on the server. The DELETE
statement works in a similar way to the GET
request, where the URL itself identifies the data.
REST is a new design pattern that has been put forward in the SOA architectures and has been widely used by enterprises because it uses the very old W3C HTTP request standards to manipulate data. Some leading social media giants, such as Twitter and Facebook, use REST to work with its services.
Internally, REST uses the routing API. The data that is passed using the REST URI is parsed by the .NET routing API, which then calls the appropriate function on the server. The main advantage of REST is that it is representational and data is in human-readable format.
A WCF Service method can be made REST-based by specifying an attribute WebInvoke
to it. The WebInvoke
attribute specifies the following properties:
BodyStyle
: This specifies the body style of messages that are sent to and from the service. The BodyStyle
property can be bare (which means the body is not wrapped inside anything) or wrapped (WrappedRequest
or WrappedResponse)
.Method
: This specifies the HTTP method that is used to send the request for the WCF interface. The WebGet
service can be used to specify GET
requests for a service as well.RequestFormat
: You are free to specify either the XML or JSON format of the request using this property.ResponseFormat
: You can use this property to specify the response format of the WCF service.UriTemplate
: This specifies the routing URL that needs to be parsed from the request URL to call a method.Once the WCF service is configured properly, the REST
methods for the server are enabled based on the configuration you have specified.
Let's take this further with some additional topics.
With the introduction of HTML5, WebSocket is one of the burning features that WCF needed badly. WCF 4.5 introduced WebSocket, which enables the browser sockets to communicate directly with the server WCF services, including the implementation of two-way communication channels directly from the browser.
Ever since web services came into being, developers have always tried to leverage their time to implement message formats in a way that makes the application run faster. The implementation of REST and JSON can be seen speeding up as we are trying to reduce the size of the data that is sent over the network, but we are still moving and parsing text to make it in a standard format, which is the slowest data transfer mechanism. Binary data gives the best output. However, as we communicate using the HTTP protocol, there is always an overhead while working over the Web.
Performance is not the only issue; HTTP is tied to the request/response cycle too. Even for asynchronous AJAX-based services, the request has to wait for a certain interval until the server can process the initial request and return the response. The browser then needs to parse the response body to generate the document object. This is a slower approach of communication.
A far better arrangement would be for a client to submit its request in a "fire and forget" kind of arrangement and then the service calls the client back with some data.
WebSockets addresses all these issues and also goes through the process of standardization. It uses TCP to communicate, giving you the benefit of a faster protocol as it works at a lower level than HTTP without additional HTTP header information. WebSockets also supports sending both binary and text formats. The best part of WebSockets is the two-way communication between the browser and the server.
To use WebSockets, the service needs to be configured with NetHttpBinding
. Let's create DuplexContract
for the WebSocket as follows:
[ServiceContract(CallbackContract=typeof(IDuplexCallbackContract))] public interface IDuplexContract { [OperationContract] string GreetMeDuplex(string name); } [ServiceContract] public interface IDuplexCallbackContract { [OperationContract] void GreetingMe(string message); }
In the preceding code, we implemented two contracts: one is the original duplex contract and the other is used as a callback. Here, the notion of execution is that the client calls GreetMeDuplex
with a name and GreetMe
from IDuplexCallbackContract
will automatically call the client with a message.
The service implementation is as follows:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)] public class WebSocketSampleService : IRegularContract, IDuplexContract { public string GreetMeDuplex(string name) { OperationContext.Current. GetCallbackChannel<IDuplexCallbackContract>().GreetintMe("Hello " + name + " by WebSockets"); return "Hello " + name; } }
So in the implementation, we call the callback with a message and return the name. The callback will call the client itself directly with the message and also return a string response from the service.
To configure a WebSocket
endpoint, we specify the endpoint binding as netHttpBinding
as follows:
<service name="WebSocketSampleService"> <endpoint address="http://localhost:8083" binding="netHttpBinding" contract="Contracts.IDuplexContract"/> </service> <bindings> <netHttpBinding> <binding name="TextOverWebSockets" messageEncoding="Text"/> </netHttpBinding> </bindings>
By specifying messageEncoding
, we indicate that the service only supports text formats here. You can also set binary message encoding when required.
Finally, to consume the WebSocket service, we just add a reference to the service on a Console Application and call the service as follows:
WebSockets.IDuplexContract duplexProxy; duplexProxy = new WebSockets.DuplexContractClient( callbackContext, "DuplexContract"); Console.WriteLine("Calling the duplex contract:"); Console.WriteLine(duplexProxy.GreetMeDuplex("ido")); // Or use a DuplexChannelFactory DuplexChannelFactory<WebSockets.IDuplexContract> dchf = new DuplexChannelFactory<WebSockets.IDuplexContract>( callbackContext, new NetHttpBinding(), new EndpointAddress("http://localhost:8083/")); duplexProxy = dchf.CreateChannel(); Console.WriteLine("Calling the duplex contract using text encoded messages:"); Console.WriteLine(duplexProxy.GreetMeDuplex("ido"));
In this way, callbackContext
automatically gets called from the duplex WebSocket. Most of the modern browsers are capable of invoking a call, and WebSockets can directly call this service from it.
3.145.17.140