In this article, I will talk about creating WCF RESTful services for CRUD operations which uses JSON and DataContracts. CRUD operations are done by HTTP verbs GET, POST, PUT and DELETE.
REST stands for Representational State Transfer. It provides you architectural principles about how client and service can exchange the resources over HTTP. REST services can be accessed by any language which supports HTTP communication and help truly to heterogeneous applications.
As REST services are sessionless, stateless resource based mechanisms it significantly reduces message size and increases performance. It provides REST API for CRUD operations and transfers JSON, XML, or both. See more details on RESTful CRUD operations using ASP.NET Web API.
JSON stands for JavaScript Object Notation. It is a lightweight data exchange format. It does not create lengthy tags like XML and produce human readable clean data. JSON is completely language independent. It gives you a collection of Name/value pair. The data member names must be a valid JSON string and included in quotation marks or apostrophes. If you are not using reserved JavaScript keyword you may omit the quotation marks or apostrophes.
Follow the article how to create wcf restful services to understand what is RESTful services, when to use which http verbs and nature of RESTful services.
Create a DataContract Orders which will be used for exchanging orders information between service and client. Add below OrderData contract to the RESTfulWCFService application created in the previous step.
using System.Runtime.Serialization;
namespace RESTFulWCFService
{
[DataContract]
public class OrderContract
{
[DataMember]
public string OrderID { get; set; }
[DataMember]
public string OrderDate { get; set; }
[DataMember]
public string ShippedDate { get; set; }
[DataMember]
public string ShipCountry { get; set; }
[DataMember]
public string OrderTotal { get; set; }
}
}
Download Orders XML file and place it in C: drive. The CRUD operations will be performed on this XML file using REST calls.
Add OrderService to your application. Right click on RESTFulWCFService application from solution explorer -> Select Add -> Select New Item -> Select WCF Service -> Name it as OrderService.
It will add two files OrderService.svc and IOrderService.cs
Open IOrderService ServiceContract. Add below OperationContracts to OrderService with WebGet or WebInvoke to make the service as RESTful.
Notice the following attributes for WebGet or WebInvoke
using System.ServiceModel.Web;
namespace RESTFulWCFService
{
[ServiceContract]
public interface IOrderService
{
[OperationContract]
[WebGet(UriTemplate = "/GetOrderTotal/{OrderID}",
ResponseFormat= WebMessageFormat.Json)]
string GetOrderTotal(string OrderID);
[OperationContract]
[WebGet(UriTemplate = "/GetOrderDetails/{OrderID}",
RequestFormat=WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
OrderContract GetOrderDetails(string OrderID);
[OperationContract]
[WebInvoke(UriTemplate = "/PlaceOrder",
RequestFormat= WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json, Method = "POST")]
bool PlaceOrder(OrderContract order);
}
}
Open OrderService.cs and implement operations from IOrderService.
using System.Xml.Linq;
using System.Collections;
namespace RESTFulWCFService
{
public class OrderService : IOrderService
{
public string GetOrderTotal(string OrderID)
{
string orderTotal = string.Empty;
try
{
XDocument doc = XDocument.Load("C:\\Orders.xml");
orderTotal =
(from result in doc.Descendants("DocumentElement")
.Descendants("Orders")
where result.Element("OrderID").Value == OrderID.ToString()
select result.Element("OrderTotal").Value)
.FirstOrDefault<string>();
}
catch (Exception ex)
{
throw new FaultException<string>
(ex.Message);
}
return orderTotal;
}
public OrderContract GetOrderDetails(string OrderID)
{
OrderContract order = new OrderContract();
try
{
XDocument doc = XDocument.Load("C:\\Orders.xml");
IEnumerable<XElement> orders =
(from result in doc.Descendants("DocumentElement")
.Descendants("Orders")
where result.Element("OrderID").Value == OrderID.ToString()
select result);
order.OrderID = orders.ElementAt(0)
.Element("OrderID").Value;
order.OrderDate = orders.ElementAt(0)
.Element("OrderDate").Value;
order.ShippedDate = orders.ElementAt(0)
.Element("ShippedDate").Value;
order.ShipCountry = orders.ElementAt(0)
.Element("ShipCountry").Value;
order.OrderTotal = orders.ElementAt(0)
.Element("OrderTotal").Value;
}
catch (Exception ex)
{
throw new FaultException<string>
(ex.Message);
}
return order;
}
public bool PlaceOrder(OrderContract order)
{
try
{
XDocument doc = XDocument.Load("C:\\Orders.xml");
doc.Element("DocumentElement").Add(
new XElement("Products",
new XElement("OrderID", order.OrderID),
new XElement("OrderDate", order.OrderDate),
new XElement("ShippedDate", order.ShippedDate),
new XElement("ShipCountry", order.ShipCountry),
new XElement("OrderTotal", order.OrderTotal)));
doc.Save("C:\\Orders.xml");
}
catch (Exception ex)
{
throw new FaultException<string>
(ex.Message);
}
return true;
}
}
}
WCF RESTful Service endpoint Configuration is about similar to normal endpoints. Notice that we are using webHttpBinding.
We added the endpointBehaviors for webHttp settings. Add below service endpoints.
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<service behaviorConfiguration="Default"
name="RESTFulWCFService.OrderService">
<endpoint address="" behaviorConfiguration="webBehavior"
binding="webHttpBinding"
contract="RESTFulWCFService.IOrderService" />
<endpoint contract="IMetadataExchange" binding="mexHttpBinding"
address="mex" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="webBehavior">
<webHttp helpEnabled="true" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="Default">
<serviceMetadata httpGetEnabled="true" />
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Right click on OrderService.svc and select View in browser. You may enter the below URL in the browser.
http://localhost:61090/OrderService.svc/GetOrderDetails/10250
Notice that DataMember attributes are shown in alphabetically order. You may set it using DataMembers Order property. Click here to know more about DataMember and enum member attributes.
Add a console application to your solution. Add a reference to WCFServices where OrderService is. Add below client code in the console application.
using System.Net;
using System.Runtime.Serialization.Json;
using RESTFulWCFService;
namespace DotnetmentorsClient
{
class Program
{
static void Main(string[] args)
{
GetOrderDetails("10248");
GetOrderTotal("10250");
PlaceOrder();
Console.ReadKey(true);
}
private static void GetOrderDetails(string orderID)
{
WebClient proxy = new WebClient();
string serviceURL =
string.Format("http://localhost:61090/OrderService.svc
/GetOrderDetails/{0}", orderID);
byte[] data = proxy.DownloadData(serviceURL);
Stream stream = new MemoryStream(data);
DataContractJsonSerializer obj =
new DataContractJsonSerializer(typeof(OrderContract));
OrderContract order = obj.ReadObject(stream) as OrderContract;
Console.WriteLine("Order ID : " + order.OrderID);
Console.WriteLine("Order Date : " + order.OrderDate);
Console.WriteLine("Order Shipped Date : " + order.ShippedDate);
Console.WriteLine("Order Ship Country : " + order.ShipCountry);
Console.WriteLine("Order Total : " + order.OrderTotal);
}
private static void GetOrderTotal(string orderID)
{
Console.WriteLine();
Console.WriteLine("**** Output for GetOrderTotal ************");
WebClient proxy = new WebClient();
string serviceURL =
string.Format("http://localhost:61090/OrderService.svc
/GetOrderTotal/{0}", orderID);
byte[] data = proxy.DownloadData(serviceURL);
Stream stream = new MemoryStream(data);
DataContractJsonSerializer obj =
new DataContractJsonSerializer(typeof(string));
string order = Convert.ToString(obj.ReadObject(stream));
Console.WriteLine(order);
}
private static void PlaceOrder()
{
OrderContract order = new OrderContract
{
OrderID = "10550",
OrderDate = DateTime.Now.ToString(),
ShippedDate = DateTime.Now.AddDays(10).ToString(),
ShipCountry = "India",
OrderTotal = "781"
};
DataContractJsonSerializer ser =
new DataContractJsonSerializer(typeof(OrderContract));
MemoryStream mem = new MemoryStream();
ser.WriteObject(mem, order);
string data =
Encoding.UTF8.GetString(mem.ToArray(), 0, (int)mem.Length);
WebClient webClient = new WebClient();
webClient.Headers["Content-type"] = "application/json";
webClient.Encoding = Encoding.UTF8;
webClient.UploadString("http://localhost:61090/OrderService.svc
/PlaceOrder", "POST", data);
Console.WriteLine("Order placed successfully...");
}
}
}
The client code for Operation PlaceOrder uses POST method which sends JSON data to service. It uses DataContractJsonSerializer to serialize DataContract. We use webClient.UploadString method to post data to service
How to Create WCFRESTFulService with jSon to perform CRUD operations on SQL Server DB . Please forward me example for it on patilrobin420@gmail.com
@Robin, You can use the same methods however you have replace the actual code which talks to your SQL Server through SQLConnection and SQLCommand
Hi Its a very good example but un fortunately i am getting an error when i post the data it give me error "The remote server returned an error: (400) Bad Request." pls can you help me out
PlaceOrder() throws the error "The remote server returned an error: (400) Bad Request."
@Viii, "The remote server returned an error: (400) Bad Request." error comes because client sends data more than set for maxReceivedMessageSize, <br /> Try to change your server side config file and set maxReceivedMessageSize = 65536000
helo
Hi, I am also facing error "The remote server returned an error: (400) Bad Request." at PlaceOrder(). does setting up value solve the problem maxReceivedMessageSize ?! How/where to set maxReceivedMessageSize in web.config?
If I want to just a post a string (128 characters long), which will be parsed at server, how can I pass it? Is it even possible? All I care about post the data which is string.
@Max you can certainly do it ... just make a operation contract which accept a string see below operation contract [OperationContract] [WebInvoke(UriTemplate = "/PlaceOrder", RequestFormat= WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, Method = "POST")] bool PlaceOrder(string ordernumber);
Thanks Laxmikant for quick response. Is that also possible to pass more than 1 parameter? Also how do I add reference in client when client and service are not in single solution? Is there also a way to pass parameters other than json format? Thanks,
@Max 1. you can have multiple parameters as input with this you also have to configure UriTemplate accordingly number of parameters. 2. You can update the ServiceUrl and call proxy.DownloadData() function which may be or may not be in same solution. 3. You can also have XML as Request / Response format.
I see that we can also set the property of object as you did in your example and that way i can set additional parameter in the property. So is it possible to pass order object from separate project? Thanks for your help.
Hi Laxmikant, Any suggestion for my last post? Thanks,
how to retrieve Particular data from JSON file. Thank u
JSON filter based upon Condition
Hi, I refer your code but the problem is that i am not getting any value at service side while placing order. Please suggest some solutions. Thank you
are you using the webclient and datacontract to place order or calling using jQuery Ajax calls. Please download the code and try.
I did same as you except I put the service and client in different projects whereas both project have their own OrderContract class. But the issue is same, i.e. I did not get any value at the service side.
if you have use different projects for web api and client then you will have to enable cors at web api and use JSONP protocol while making ajax calls
The restful WCF integration model is really messy in terms of configuration paradigm and thus fraught with lots of if's and buts. Anyways, if anybody wants to know why you can get (400) bad request is generic error, it is because the solution is hardcoded to check for Order.xml from c:\ drive. As soon as you correct the location, the solution should work like a charm. My experience on the WWF stack was better then WCF, specially the REST on WCF is not very intuitive and defined for developers. Too many what-iff's. But this is one solution among maybe 15-20 i saw and the comments from others which helped me tackle some of the ghost in my config to support both Soap and Rest on the same Service. Kudos to the author
Thanks FiFa for providing your experience. Have you change to relative path to Orders.xml?
A very useful piece of information thank you
do i need url of incoming REST JSON data before i can read it using vb.net
A very useful piece of information thank you, congratulations...
Kindly help me understand where you have setup the port number 61090. I would like to use port 8090 instead. Kindly help me. Festus
Festus, that is just random number chosen by service, if you hasted service in IIS you can change port in add site bindings or if you have not hosted you can update project properties to set port