Archive for May, 2012

ASP.NET 4.0 WCF RESTful Services – Part 2

In the first part, we learned how to setup a ASP.NET 4.0 WCF (file-extension less) service and the basics on using jQuery’s $.getJSON() method to call a service implementing the GET method. Now, we are going to review all the HTTP methods commonly found in a RESTful api (GET, POST, PUT, and DELETE). The code below targets the project created in step 1, which I’ll update in my next post. Each HTTP methods/verb is used to describe a process in a CRUD transaction, so I tried to keep things simple by naming accordingly. Everything below was written in Visual Studio 2010 with .NET 4.0.

RESTful HTTP Methods ( GET / POST / PUT / DELETE )

Before we explore the HTTP methods, remember the following rules while building your WCF Services.

The UriTemplate can only use parameters of type string

Uri Templates are very flexible and you can build your own standard syntax for your api.
Sample Uri Templates for .svc-less function calls

[WebGet(UriTemplate = "Products/{name}")]
public Product GetProduct(string name)
// URL = "/InventoryService/Products/ProductXYZ"
[WebGet(UriTemplate = "Products/Product({name})")]
public Product GetProduct(string name)
// URL = "/InventoryService/Products/Product(ProductXYZ)"
[WebGet(UriTemplate = "Products/API.Service?Product={name}")]
public Product GetProduct(string name)
// URL = "/InventoryService/Products/API.Service?Product=ProductXYZ"

When passing multiple objects to a service, you must wrap the objects.
** Difference sending 1 object vs 2 objects **

//  Sending 1 Object (No Wrapping).
[WebInvoke(UriTemplate = "Products/API.Service?Product={productName}", Method = "POST")]
public string APIGet(string productName, Product product1)
// Sending 2 Objects (Wrapping)
// Added "BodyStyle = WebMessageBodyStyle.WrappedRequest".
[WebInvoke(UriTemplate = "Products/API.Service?Product={productName}", Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest )]
public string APIGet(string productName, Product product1, Product product2)

jQuery Ajax Call (Sending 1 object vs 2 objects)

// Common Objects to use in examples below
var Product1 = {};
Product1.Name = 'Name1';
Product1.Description = 'Desc1';
Product1.Price = 1.0;
Product1.Quantity = 1;
Product1.IsDiscontinued = false;
var Product2 = {};
Product2.Name = 'Name2';
Product2.Description = 'Desc2';
Product2.Price = 2.0;
Product2.Quantity = 2;
Product2.IsDiscontinued = false;
//  Sending 1 Object (No Wrapping).
$.ajax({
    type: 'POST',
    url: '/InventoryService/Products/API.Service?Product=ProductXYZ',
    contentType: 'application/json; charset=utf-8',
    dataType: 'json',
    data: JSON.stringify(Product1),  // Here we are sending the 1 object, no wrapper.
    success: function (response)
    {
        // Do Something
    }
});
// Sending 2 Objects (Wrapping)
// Here we are creating a new jsonRequest object that wraps our 2 objects that we want to send to the server.
jsonRequest = { 'product1': Product1, 'product2': Product2 };
$.ajax({
    type: 'POST',
    url: '/InventoryService/Products/API.Service?Product=ProductXYZ',
    contentType: 'application/json; charset=utf-8',
    dataType: 'json',
    data: JSON.stringify(jsonRequest),  // Here we are sending a object that wraps our 2 product objects.
    success: function (response)
    {
        // Do Something
    }
})

GET / POST / PUT / DELETE WCF Services and their jQuery Ajax Calls

GET Method

Service

[WebGet(UriTemplate = "Products/{name}")]
[OperationContract]
public Product GetProduct(string name)
{
	// Get a Product
}

jQuery Call

var name = 'ProductXYZ';

$.ajax({
	type: 'GET',
	url: '/InventoryService/Products/' + name,
	contentType: 'application/json; charset=utf-8',
	dataType: 'json',
	data: '',
	success: function (response)
	{
		// Do Something
	}
});

// Alternative to $.ajax for a GET request is using $.getJSON.
$.getJSON('/InventoryService/' + name, '', function (result) { // Do Something });

This is probably the simplest service call to issue, but the amazing jQuery team made the process even easier with the getJSON() call that wraps up a ajax() GET request. Notice the svc-less call above, there’s no ugly “.svc” telling your consumers… Hay, I’m using WCF!!!

POST Method

Service

[WebGet(UriTemplate = "Products/{name}", Method = "POST")]
[OperationContract]
public Product GetProduct(string name, Product product)
{
	// Insert New Product
}

jQuery Call

var name = 'ProductXYZ';
var Product = {};
Product.Name = 'ProductXYZ';
Product.Description = 'Product XYZ Description';

$.ajax({
	type: 'POST',
	url: '/InventoryService/Products/' + name,
	contentType: 'application/json; charset=utf-8',
	dataType: 'json',
	data: JSON.stringify(Product),
	success: function (response)
	{
		// Do Something
	}
});

This has always been my go to method for sending data to a service, but according to a few RESTful people this method is only supposed to be used for inserts. I’m not really sure how “firm” this rule is, so I decided to do all Inserts and Updates with the POST method, since I almost always handle this in the same function call in my DAL. In addition to posting my Object to the service, I also include the primary key (“PK”) in the service path so I can use the IIS logs to show who touched a specific record.

PUT Method

Service

[WebInvoke(UriTemplate = "Products/{name}", Method = "PUT")]
[OperationContract]
public Product GetProduct(string name)
{
	// Update Product
}

jQuery Call

var name = 'ProductXYZ';
var Product = {};
Product.Name = 'ProductXYZ';
Product.Description = 'Product XYZ Description';

$.ajax({
	type: 'PUT',
	url: '/InventoryService/Products/' + name,
	contentType: 'application/json; charset=utf-8',
	dataType: 'json',
	data: JSON.stringify(Product),
	success: function (response)
	{
		// Do Something
	}
});

I don’t use this much, but when I do the calls look exactly the same as a POST except for the change in request type.

DELETE Method

Service

[WebGet(UriTemplate = "Products/{name}", Method = "DELETE")]
[OperationContract]
public bool GetProduct(string name)
{
	// Delete Product
}

jQuery Call

var name = 'ProductXYZ';

$.ajax({
	type: 'DELETE',
	url: '/InventoryService/Products/' + name,
	contentType: 'application/json; charset=utf-8',
	dataType: 'json',
	data: '',
	success: function (response)
	{
		// Do Something
	}
});

Since I always like “confirmation” when something has been done, I always return a boolean from my delete services. This allows me to confirm something was able to be deleted, if you expect to have various failure states you might want to consider using a string return type to provide a detailed “error” message whey the request failed (e.g. “Successfully Deleted”, “You don’t have permission.”, “Product is associated with other orders.”).

ASP.NET 4.0 WCF RESTful Services – Part 1

Details, details, details… I just spend a few days moving over to svc-less WCF services with a ASP.NET 4.0 web application, and boy was it fun… The overall setup is pretty easy, once you’ve painfully gone through the process “a few” times. Since this is something I’ll no be doing by default on all new projects, I thought this would make a great write-up tutorial. During my discovery and learning phase, I found a bunch of helpful blog posts but nothing was 100% and there was lots of those “important” bits missing. Since most of my projects consume JSON, I plan to do a 4 part series on setting up a Web Application to support WCF services that will be called via jQuery.

Enough with the background, let’s start by creating a new “ASP.NET Web Application”.

What project to choose in VS 2010

  1. Remove everything inside the Scripts folder.
  2. Right Click on Project, and choose “Manage NuGet Packages…”

    Hopefully your familiar with NuGet, it’s a package manager for Visual Studio and you can install it by going to Tools -> Extension Manager… -> Online Gallery -> Download “NuGet Package Manager”. This is the most popular extension and it allows you to quickly add bits to your projects (e.g. Install jQuery, and be notified when a new version is available.).

  3. Use search to install the following NuGet Packages.
    • jQuery
    • JSON-js json2

    Required NuGet Packages

  4. Edit your “site.master” to and include your new scripts:
    	   <script src="Scripts/jquery-1.7.2.js" type="text/javascript"></script>
    	   <script src="Scripts/jquery-ui-1.8.19.js" type="text/javascript"></script>
    	   <script src="Scripts/jquery.validate.js" type="text/javascript"></script>
    	
  5. Also in the “site.master”, edit your asp:Menu to include 4 pages called Part1 – Part4:
                    <asp:Menu ID="NavigationMenu" runat="server" CssClass="menu" EnableViewState="false" IncludeStyleBlock="false" Orientation="Horizontal">
                        <Items>
                            <asp:MenuItem NavigateUrl="~/Default.aspx" Text="Home"/>
                            <asp:MenuItem NavigateUrl="~/Part1.aspx" Text="Part 1"/>
                            <asp:MenuItem NavigateUrl="~/Part2.aspx" Text="Part 2" />
                            <asp:MenuItem NavigateUrl="~/Part3.aspx" Text="Part 3" />
                            <asp:MenuItem NavigateUrl="~/Part3.aspx" Text="Part 4" />
                            <asp:MenuItem NavigateUrl="~/About.aspx" Text="About" />
                        </Items>
                    </asp:Menu>
    	

    ** We are only going to use Part1.aspx right now, but I plan on 4 posts on this topic.

  6. Add 4 new “Web Form using Master Page” to the project called Page1.aspx, Page2.aspx, Page3.aspx, Page4.aspx.
    ** These will match the named used in the navigation menu in the site.master.
  7. Add a new folder to the root of the project called Service.
  8. Add a new “AJAX-enabled WCF Service” to the Service folder called “InventoryService.svc”.
    • Note, this will add the following references to your project.
      	     System.Runtime.Serialization
      	     System.ServiceModel
      	     System.ServiceModel.Web
      	
    • It will also add the following lines to your Web.config
      	  <system.webServer>
      		 <modules runAllManagedModulesForAllRequests="true"/>
      	  </system.webServer>
      
      	  <system.serviceModel>
      		<behaviors>
      		  <endpointBehaviors>
      			<behavior name="RESTfulWCF.Service.Service1AspNetAjaxBehavior">
      			  <enableWebScript />
      			</behavior>
      		  </endpointBehaviors>
      		</behaviors>
      		<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
      		  multipleSiteBindingsEnabled="true" />
      		<services>
      		  <service name="RESTfulWCF.Service.Service1">
      			<endpoint address="" behaviorConfiguration="RESTfulWCF.Service.Service1AspNetAjaxBehavior"
      			  binding="webHttpBinding" contract="RESTfulWCF.Service.Service1" />
      		  </service>
      		</services>
      	  </system.serviceModel>
      	
    • Change the “system.serviceModel” section of your web.config to the following.
      	    <system.serviceModel>
      	        <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
      	        <standardEndpoints>
      	            <webHttpEndpoint>
      	                <standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
      	            </webHttpEndpoint>
      	        </standardEndpoints>
      	    </system.serviceModel>
      	

      Web.Config Key Points

      	// This allows your services to run under your sites app pool, giving access
      	// to your HttpContext.
      	aspNetCompatibilityEnabled="true"
      	
      	// See below, this allows you to type <service>/help to get information on your service	
      	helpEnabled="true"
      	
      	// This is AWESOME, this tag allows your service to respond in any format you 
      	// specify in your request (e.g. XML, JSON, etc...).
      	automaticFormatSelectionEnabled="true"
      	

      ** One last item to note, every time you add an additional AJAX service, it will edit your web.config and put back in the bad configuration. I strongly suggest you make a backup of your web.config, incase you run into problems in the future!!!

    • Manually add a project reference to System.ServiceModel.Activation.
  9. At this point, your project in solution explorer should look like this:

    Items Included in Solution Explorer

  10. Now, open InventoryService.svc and make the following changes:
    • Erase everything and add the following C# code:
      using System.ServiceModel;
      using System.ServiceModel.Activation;
      using System.ServiceModel.Web;
      
      namespace RESTfulWCF.Service
      {
          [ServiceContract(Namespace = "")]
          [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
          public class InventoryService
          {
              [WebGet(UriTemplate = "Part1")]
              [OperationContract]
              public string Part1GetRequest()
              {
                  return "I did work";
              }
          }
      }
      	

      In the code above, we are mapping the function “Part1GetRequest” to the name “Part1”. This will allow us to call the service with the following syntax “/InventoryService/Part1” using a GET request.

  11. Add a route to call the service without referencing a “.svc” file.
    Open Global.asax and replace your Applicaiton_Start with the following.

            void Application_Start(object sender, EventArgs e)
            {
                // Code that runs on application startup
                RouteTable.Routes.Add(new ServiceRoute("InventoryService", new WebServiceHostFactory(), typeof(Service.InventoryService)));
            }
    	
  12. Now we are ready to create a jQuery test call, open “Page1.aspx” in Source View:

    Erase everything and add the following HTML code

    <%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Part1.aspx.cs" Inherits="RESTfulWCF.Part1" %>
    <asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
    </asp:Content>
    <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
        <script type="text/javascript">
            $(document).ready(function ()
            {
                $('#doWork').click(function ()
                {
                    $.getJSON('/InventoryService/Part1', '', function (result) { alert(result); });
                });
            });
        </script>
        <h2>
            ASP.NET 4.0 WCF RESTful Demo - Part 1
        </h2>
        <p>
            <input type="button" id="doWork" value="Issue GET Request" />
        </p>
    </asp:Content>
    	

    We are able to use the $.getJSON() jQuery function because we applied the “WebGet” attribute to the WCF function “Part1GetRequest()”.

  13. Launch the application and navigate to “Page 1”. Click on the “Issue GET Request” button and you should see the followign results:

    jQuery Demo Results

  14. To get a list of all the functions your service offers, pass the “/help” parameter to your service.

    List of all WCF Service Accessible by Passing /help to the Service

As noted, here is the project I built above following all the steps one-by-one.
    >> ASP.NET 4.0 RESTful Web Application Project