fredag 2 maj 2008

Calling WCF from jQuery using parameters


This continues our discoveries in the articles "Returning true JSON from WCF services", "Return JSON from Ajax-enabled WCF Service" and "Using jQuery with JSon-enabled WCF Services".

We will now add two parameters to our DoWork-method and supply them when we call them using jQuery.


Open up the "AjaxEnabled.svc.cs" and add two parameters, string name and int id:


using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Web;
 
namespace Alaz.DotNetDiscoveries.JSONWithWCF
{
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class AjaxEnabled
    {
        // Add [WebGet] attribute to use HTTP GET
        [OperationContract]
        [WebGet(ResponseFormat=WebMessageFormat.Json,BodyStyle = WebMessageBodyStyle.Bare)]
        public MyClass DoWork(string name, int id)
        {
            var returnObject = new MyClass
            {
                //Use our fresh parameters:
                Id = id,
                Name = name,
                //Added a call to HttpContext.Current:
                NoPublicGet = HttpContext.Current.Request.RawUrl,
                IHaveNoDataMemberAttribute = "Meaningless",
                InternalProperty = 155
            };
            return returnObject;
        }
 
        // Add more operations here and mark them with [OperationContract]
    }
}


Then try to run it with the url "AjaxEnabled.svc/DoWork?name=Zlatan&id=8". Your answer should be:

{"Id":8,"InternalProperty":155,"Name":"Zlatan","NoPublicGet":"\/AjaxEnabled.svc\/DoWork?name=Zlatan&id=8"}


Make (or add to our previous example) a web page like this (note that in our example there is no check that the number-field only provides an integer):


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Test jQuery, WCF and JSon</title>
    <script src="script/jquery-1.2.2.js" type="text/javascript"></script>
    <script type="text/javascript">
    $().ready(function()    
    {
        $("#myButt").click(function()
        {
            $.getJSON("TrueJSON.svc/DoWork",function(data)
            {
                alert("Name=" + data.Name + "\nId=" + data.Id + "\nNoPublicGet="+data.NoPublicGet + "\nInternalProperty=" + data.InternalProperty );
            });
        });
 
        $("#parameterButt").click(function()
        {
            var nameValue=$("#name").val();
            var idValue=$("#id").val();
            $.getJSON("AjaxEnabled.svc/DoWork",{name:nameValue,id:idValue},function(data)
            {
                alert("Name=" + data.Name + "\nId=" + data.Id + "\nNoPublicGet="+data.NoPublicGet + "\nInternalProperty=" + data.InternalProperty );
            });
        });
    });
    </script>
</head>
<body>
<input type="button" value="Get values from server" id="myButt" />
<p>
<input type="text" id="name" value="Zlatan" /><br />
<input type="text" id="id" value="8" /><br />
<input type="button" value="Get values from server, call with parameters" id="parameterButt" />
</p>
</body>
</html>



Nice, isn't it?

That concludes this article-series about using WCF services with jQuery.

Good luck!

Using jQuery with JSon-enabled WCF Services

This continues our discoveries in the articles "Returning true JSON from WCF services" and "Return JSON from Ajax-enabled WCF Service", and will explain how we call our service from jQuery using the $.getJSON-call.


Download the .js file from http://jquery.com/, make a html or aspx-page and point a script-src-tag to the downloaded script.

Below I have added a simple jQuery-script that gets our example-data from the WCF service and alerts the results.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Test jQuery, WCF and JSon</title>
    <script src="script/jquery-1.2.2.js" type="text/javascript"></script>
    <script type="text/javascript">
    $().ready(function()    
    {
        $("#myButt").click(function()
        {
            $.getJSON("TrueJSON.svc/DoWork",function(data)
            {
                alert("Name=" + data.Name + "\nId=" + data.Id + "\nNoPublicGet="+data.NoPublicGet + "\nInternalProperty=" + data.InternalProperty );
            });
        });
    });
    </script>
</head>
<body>
<input type="button" value="Get values from server" id="myButt" />
</body>
</html>

Simple, isn't it?

If you are unfamiliar with the jQuery syntax, read more about it on http://jquery.com/.


Ok, lets move on, now we are going to wrap things up and show how we can send in some parameters to our WCF Service in the article "Calling WCF from jQuery using parameters".


Accessing HttpContext.Current from WCF Service


By default, when you create an new WCF Service in a web site, they live side-by-side and the WCF service cannot access the HttpContext.Current-object (and the other ASP.NET-features like File/URL-authorization, HttpModules and ASP.NET impersonation), and in some Ajax-scenarios, we really need it.


There are two simple steps for enabling it.

First, you need to turn it on in the web config, look for "system.serviceModel" and add the "serviceHostingEnvironment"-tag like this:


  <system.serviceModel>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />


Second, add the attribute "[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]" to your service-class (it can't be in the interface):

using System.ServiceModel.Activation;
 
namespace Alaz.DotNetDiscoveries.JSONWithWCF
{
    // NOTE: If you change the class name "TrueJSON" here, you must also update the reference to "TrueJSON" in Web.config.
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    public class TrueJSON : ITrueJSON
    {
        public MyClass DoWork()
        {
            var returnObject = new MyClass 
                {
                    Id = 1, 
                    IHaveNoDataMemberAttribute = "Meaningless", 
                    Name = "Satchmo",
                    NoPublicGet = "Hello world!",
                    InternalProperty = 155
                };
            return returnObject;
        }
    }
}

And you are done!


MSDN documentation here.


Return JSON from Ajax-enabled WCF Service


This is another way to return JSON from WCF than I described in the previous article "Returning true JSON from WCF services".

As in that article, this method was discovered using trail-and-error, and some Googling. I have not studied WCF in depth, I just barely learn what I need to, so there may very well be some better way to do it, I just haven't been able to find it yet.



  1. In a web project, "Add new item.." and choose "Ajax-enabled WCF Service", let's call it "AjaxEnabled.svc"

  2. This service is made without an interface to specify the methods (i.e. no "IAjaxEnabled.cs" was created as it would have done if you choose to add a new WCF Service like in the previous article), and consists of the single method DoWork().

  3. Let's create something to return, add the "MyClass" from the other article and replace the method DoWork() from there too.

    Add the [WebGet] attribute too, since there doesn't seem to be a $.postJSON-function in jQuery.

    You should now have an AjaxEnabled.svc.cs that looks like:



    using System.ServiceModel;
    using System.ServiceModel.Activation;
    using System.ServiceModel.Web;
     
    namespace Alaz.DotNetDiscoveries.JSONWithWCF
    {
        [ServiceContract(Namespace = "")]
        [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
        public class AjaxEnabled
        {
            // Add [WebGet] attribute to use HTTP GET
            [OperationContract]
            [WebGet]
            public MyClass DoWork()
            {
                var returnObject = new MyClass
                {
                    Id = 1,
                    IHaveNoDataMemberAttribute = "Meaningless",
                    Name = "Satchmo",
                    NoPublicGet = "Hello world!",
                    InternalProperty = 155
                };
                return returnObject;
            }
     
            // Add more operations here and mark them with [OperationContract]
        }
    }

  4. Let's try it out, run the project and call the service with adding /DoWork to the AjaxEnabled.svc-url. You should've got:



    <MyClass xmlns="http://schemas.datacontract.org/2004/07/Alaz.DotNetDiscoveries.JSONWithWCF" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <Id>1</Id>
      <InternalProperty>155</InternalProperty>
      <Name>Satchmo</Name>
      <NoPublicGet>Hello world!</NoPublicGet>
    </MyClass>


    Not really what we've wanted, apparently MS Ajax implementation use xml to communicate. Time to fiddle with some settings then:

  5. Add the ResponseFormat-property to WebGet, and set it to Json (default seems to be xml):

            [WebGet(ResponseFormat=WebMessageFormat.Json)]


  6. Run again, inspect the response in notepad, and now we got some Json, but not really in the format we would like to:

    {"d":{"__type":"MyClass:#Alaz.DotNetDiscoveries.JSONWithWCF","Id":1,"InternalProperty":155,"Name":"Satchmo","NoPublicGet":"Hello world!"}}

    There seem's to be some envelope around it. Let's remove it:

  7. Set the BodyFormat-property to Bare, we do not wan't to have Wrapped messages, only bare Json:

            [WebGet(ResponseFormat=WebMessageFormat.Json,BodyStyle = WebMessageBodyStyle.Bare)]


  8. Run again, and you will get an error:

    Server Error in '/' Application.

    --------------------------------------------------------------------------------

    The body style 'Bare' is not supported by 'WebScriptEnablingBehavior'. Change the body style to be 'WrappedRequest'.

  9. We must change something in the web.config, look at this tag:



        <behavior name="Alaz.DotNetDiscoveries.JSONWithWCF.AjaxEnabledAspNetAjaxBehavior">
         <enableWebScript />

        </behavior>   




  10. Change "enableWebScript" to "webHttp" like this:
        <behavior name="Alaz.DotNetDiscoveries.JSONWithWCF.AjaxEnabledAspNetAjaxBehavior">
         <webHttp />
        </behavior>

    The behavior is connected to:
       <service name="Alaz.DotNetDiscoveries.JSONWithWCF.AjaxEnabled">
        <endpoint address="" behaviorConfiguration="Alaz.DotNetDiscoveries.JSONWithWCF.AjaxEnabledAspNetAjaxBehavior"
         binding="webHttpBinding" contract="Alaz.DotNetDiscoveries.JSONWithWCF.AjaxEnabled" />
       </service>


  11. Run again, and now you got the correct Json-string {"Id":1,"InternalProperty":155,"Name":"Satchmo","NoPublicGet":"Hello world!"}.

  12. One more thing worth mention is the AspNetCompatibilityRequirements-attribute to the service-class:

        [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

    If you want to access the current HttpContext, using ASP.NET's file/url authorization and that normal ASP.NET-stuff you must use either AspNetCompatibilityRequirementsMode.Allowed as in this example, or AspNetCompatibilityRequirementsMode.Required. My advice is to use Required if you need any of the ASP.NET-features, then the service throws an error if it is not enabled. You must enable this in the web.config (however, the "Add new Ajax-enabled WCF service" seems to add it automagically):



      <system.serviceModel>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
    If "Allowed" you can run it even if web.config says "false", and you could have a hard time finding out why HttpContext.Current is null.