For this tutorial, we'll create a WCF RESTful application that will handle HTTP POST - HTTP PUT & HTTP DELETE requests. A simpler tutorial including only HTTP GET requests can be found HERE.
The REST architecture relies upon handling HTTP requests according to this methods : GET for reading data, POST for creating a new record, PUT for updating ALL the fields of a record, MERGE (or PUT again) for updating just part of the fields of some record, and DELETE for removing a record.
We'll see the data modifications at an MVC application, which will use an SQL database represented by the Entity Data Model EDM, showing as follows:
We'll add to that MVC application a WCF Web Service to fetch and preserve data & render it in JSON format.
Now add a new item to your project :
Search for all WCF items, and select AJAX-enabled WCF :
Open the web.config created: you'll recognize an ENDPOINT BEHAVIOUR to enable Web Script , that means, AJAX jquery calls, and a webHttpBinding (for REST purposes) ENDPOINT linked to a CONTRACT named "BlogService" :
The "<enableWebScript>" allows our RESTful WCF service to be accessed through AJAX calls, and this option includes also the "<WebHttp>" directive, which transforms the WCF service in RESTful, based on HTTP verbs which determine the CRUD operation to perform (PUT for update, POST for create, GET for retrieve, DELETE for remove).
Because at the present we don't want the RESTful WCF service to handle AJAX requests, we'll replace it with the "WebHttp" directive in the Endpoint Behavior:
Here i added a Service Behavior just to see the errors , but you can delete the behavior if you want.
Because we are inside an MVC application, we must tell MVC to ignore the route to the WCF: add this code to the RouteConfig.cs file, at the "App_Start " folder:
Open that "BlogService" class : it have got an SERVICE CONTRACT attribute :
Delete the function , and create our own Service Contract with this Interface for ALL CRUD operations:
[ServiceContract(Namespace = "")]
public interface IBlogService
{
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
List<Blog> GetPosts();
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
Blog GetPost(string Id);
[OperationContract]
[WebInvoke(Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
bool CreatePost(Blog post);
[OperationContract]
[WebInvoke(Method = "PUT",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
bool UpdatePost(Blog post);
[OperationContract]
[WebInvoke(Method = "DELETE",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
bool RemovePost(Blog post);
}
Notice we serialize the data to JSON using the Request/Response Format. Also, we set the "WebInvoke" to POST, PUT & DELETE. And the return of the web HTTP GET methods is a LIST<> or a Blog object, always in JSON format.
Now we implement the Service Contract with the following class:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class BlogService : IBlogService
{
BlogEntities ctx;
public BlogService()
{
ctx = new Models.BlogEntities();
ctx.Configuration.ProxyCreationEnabled = false;
}
public List<Blog> GetPosts()
{
List<Blog> data = ctx.Blogs.ToList();
return data;
}
public Blog GetPost(string Id)
{
int id = Convert.ToInt32(Id);
Blog data = ctx.Blogs.Where(b => b.BlogID == id).FirstOrDefault();
return data;
}
public bool CreatePost(Blog post)
{
ctx.Blogs.Add(post);
return ctx.SaveChanges() == 1;
}
public bool UpdatePost(Blog post)
{
Blog oldPost = ctx.Blogs.Where(b => b.BlogID == post.BlogID).Single();
oldPost = oldPost.Merge(post);
ctx.Blogs.Attach(oldPost);
ctx.Entry(oldPost).State = System.Data.EntityState.Modified;
return ctx.SaveChanges() == 1;
}
public bool RemovePost(Blog post)
{
ctx.Entry(post).State = System.Data.EntityState.Deleted;
return ctx.SaveChanges() == 1;
}
}
public static class Utils
{
public static Blog Merge(this Blog post, Blog modified)
{
if (modified.Title != "")
{
post.Title = modified.Title;
}
if (modified.Text != "")
{
post.Text = modified.Text;
}
return post;
}
}
Notice we add the statement "proxycreationenabled" = false, at the instantiation of the Entities Model. That's to avoid being faced with an exception:
That error is called "CIRCULAR REFERENCE WAS DETECTED WHILE SERIALIZING AN OBJECT OF TYPE" :
Also, notice that i coded an extension method to MERGE the original record with the updated one.
Now open the browser and test the GetPosts method:
Do the same for the GetPost method:
Now we test the RESTful WCF service with HTTP POST, PUT and DELETE requests, using Fiddler (You can learn Fiddler in this tutorial.).
We'll start with the HTTP POST request. Check the HTTP POST method handler; it expects an object of Blog type, that means that we'll send a POST request containing an JSON object (as specified at the Operation Contract, inside the interface) using Fiddler. WCF will automatically try to bind the JSON object to the Blog object, according to the field names:
Type the URL with the service name and the operation name, and select the "POST" method from the list:
Also, set a Content-Type as "application/json". The Content-Length is filled by Fiddler. Insert a breakpoint at the WCF to see the binded Blog post:
Then you can see the response at Fiddler:
Next, we'll test the WCF with an HTTP PUT request. At the Fiddler Composer, enter the URI and select
the "PUT" option:
Here is very important to send to the WCF web service the ID of the entry to update, together with the fields to change. Take care to respect the JSON notation : { "key":"value" , "key":"value" }
At the WCF operation, set a breakpoint:
The second line of code contains a call to the Merge() extension method, which checks for every modified field and updates it at the original object:
As you see, the old entry has been modified:
We attach the modified entry to the context, and set the state as "modified":
Then Fiddler gets the response:
We could also have sent a response containing an "OK" or "CREATED" status, as follows:
return new HttpResponseMessage(HttpStatusCode.OK);
Here i added a Service Behavior just to see the errors , but you can delete the behavior if you want.
Because we are inside an MVC application, we must tell MVC to ignore the route to the WCF: add this code to the RouteConfig.cs file, at the "App_Start " folder:
Open that "BlogService" class : it have got an SERVICE CONTRACT attribute :
Delete the function , and create our own Service Contract with this Interface for ALL CRUD operations:
[ServiceContract(Namespace = "")]
public interface IBlogService
{
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
List<Blog> GetPosts();
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
Blog GetPost(string Id);
[OperationContract]
[WebInvoke(Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
bool CreatePost(Blog post);
[OperationContract]
[WebInvoke(Method = "PUT",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
bool UpdatePost(Blog post);
[OperationContract]
[WebInvoke(Method = "DELETE",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
bool RemovePost(Blog post);
}
Notice we serialize the data to JSON using the Request/Response Format. Also, we set the "WebInvoke" to POST, PUT & DELETE. And the return of the web HTTP GET methods is a LIST<> or a Blog object, always in JSON format.
Now we implement the Service Contract with the following class:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class BlogService : IBlogService
{
BlogEntities ctx;
public BlogService()
{
ctx = new Models.BlogEntities();
ctx.Configuration.ProxyCreationEnabled = false;
}
public List<Blog> GetPosts()
{
List<Blog> data = ctx.Blogs.ToList();
return data;
}
public Blog GetPost(string Id)
{
int id = Convert.ToInt32(Id);
Blog data = ctx.Blogs.Where(b => b.BlogID == id).FirstOrDefault();
return data;
}
public bool CreatePost(Blog post)
{
ctx.Blogs.Add(post);
return ctx.SaveChanges() == 1;
}
public bool UpdatePost(Blog post)
{
Blog oldPost = ctx.Blogs.Where(b => b.BlogID == post.BlogID).Single();
oldPost = oldPost.Merge(post);
ctx.Blogs.Attach(oldPost);
ctx.Entry(oldPost).State = System.Data.EntityState.Modified;
return ctx.SaveChanges() == 1;
}
public bool RemovePost(Blog post)
{
ctx.Entry(post).State = System.Data.EntityState.Deleted;
return ctx.SaveChanges() == 1;
}
}
public static class Utils
{
public static Blog Merge(this Blog post, Blog modified)
{
if (modified.Title != "")
{
post.Title = modified.Title;
}
if (modified.Text != "")
{
post.Text = modified.Text;
}
return post;
}
}
Notice we add the statement "proxycreationenabled" = false, at the instantiation of the Entities Model. That's to avoid being faced with an exception:
That error is called "CIRCULAR REFERENCE WAS DETECTED WHILE SERIALIZING AN OBJECT OF TYPE" :
Now open the browser and test the GetPosts method:
Do the same for the GetPost method:
Now we test the RESTful WCF service with HTTP POST, PUT and DELETE requests, using Fiddler (You can learn Fiddler in this tutorial.).
We'll start with the HTTP POST request. Check the HTTP POST method handler; it expects an object of Blog type, that means that we'll send a POST request containing an JSON object (as specified at the Operation Contract, inside the interface) using Fiddler. WCF will automatically try to bind the JSON object to the Blog object, according to the field names:
Type the URL with the service name and the operation name, and select the "POST" method from the list:
Also, set a Content-Type as "application/json". The Content-Length is filled by Fiddler. Insert a breakpoint at the WCF to see the binded Blog post:
Then you can see the response at Fiddler:
Next, we'll test the WCF with an HTTP PUT request. At the Fiddler Composer, enter the URI and select
Here is very important to send to the WCF web service the ID of the entry to update, together with the fields to change. Take care to respect the JSON notation : { "key":"value" , "key":"value" }
At the WCF operation, set a breakpoint:
As you see, the old entry has been modified:
We attach the modified entry to the context, and set the state as "modified":
Then Fiddler gets the response:
return new HttpResponseMessage(HttpStatusCode.OK);
return new HttpResponseMessage(HttpStatusCode.Created);
Just change the return type from bool to HttpResponseMessage.
In case of problem we could send this instead:
return new HttpResponseMessage(HttpStatusCode.BadRequest);
return new HttpResponseMessage(HttpStatusCode.NotFound);
Finally, we'll remove an entry. All that the operation does is to set the state as "deleted", so that SaveChanges() will erase it from database:
Create the HTTP request at Fiddler, as before, but selecting "DELETE" :
Take care of sending the ID of the entry to remove. Rest of the fields are just optional, you don't need to set them:
As you can see at the Headers, the HTTP DELETE got a response of "true":
Just change the return type from bool to HttpResponseMessage.
We have tested our RESTful WCF service.
You can see more about sending HTTP requests from Fiddler in this tutorial.
Happy programming.....
By Carmel Shvartzman
Just change the return type from bool to HttpResponseMessage.
In case of problem we could send this instead:
return new HttpResponseMessage(HttpStatusCode.BadRequest);
return new HttpResponseMessage(HttpStatusCode.NotFound);
Finally, we'll remove an entry. All that the operation does is to set the state as "deleted", so that SaveChanges() will erase it from database:
Create the HTTP request at Fiddler, as before, but selecting "DELETE" :
Take care of sending the ID of the entry to remove. Rest of the fields are just optional, you don't need to set them:
As you can see at the Headers, the HTTP DELETE got a response of "true":
Again, we could also have sent a response containing an "OK" status, as follows:
return new HttpResponseMessage(HttpStatusCode.OK);
return new HttpResponseMessage(HttpStatusCode.OK);
Just change the return type from bool to HttpResponseMessage.
You can see more about sending HTTP requests from Fiddler in this tutorial.
That's all
In this post we've seen how to create a WCF RESTful service with support for all CRUD operations in Asp.Net MVC in 15 minutes. Happy programming.....
By Carmel Shvartzman
כתב: כרמל שוורצמן
No comments:
Post a Comment