Saturday, August 29, 2009

Calling a WCF Service from JavaScript

I spent the last couple of days working with SketchUp, WCF, and JavaScript.  SketchUp is a really cool tool for creating, modifying, and sharing 3D models.  I am certainly not a modeling expert but I do have some experience working with 3D models and I have to say I am was impressed with the free version.  The pro version is only $500.  While this is not a trivial sum of money, it is very affordable compared to some of the packages I have seen.

 

My job was to create a web service that could be called using JavasSript.  I will mention that this was a proof of concept and I specifically did not address security.  The following code will work but there is no security of any kind so if the resulting service is exposed to the internet, anybody can call it.   

 

The first thing I did was use the create WCF project wizard.  I wasn’t particularly worried about passing  complex data types at this point so I defined a simple Service Contract.



Code Snippet



  1. using System.ServiceModel;
  2. using System.ServiceModel.Web;
  3. namespace WcfModelService
  4. {
  5.     [ServiceContract(Namespace = "WcfModelService")]
  6.     public interface IModel
  7.     {        
  8.         [OperationContract]
  9.         [WebGet]
  10.         string GetData(int value);
  11.         [OperationContract]
  12.         [WebGet]
  13.         string HelloWorld();
  14.         [OperationContract]
  15.         [WebGet]
  16.         string HelloSomeone(string message);
  17.     }
  18. }




When you apply the WebGet attribute to  a service contract decorated with the OperationContract attribute you are adding some extra metadata to the operation.  This metadata doesn’t actually do anything unless a service behavior is looking for it.

Here is the implementation:



Code Snippet



  1. using System.ServiceModel.Activation;
  2. namespace WcfModelService
  3. {
  4.     [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
  5.     public class Model : IModel
  6.     {
  7.         #region IModel Members
  8.         public string GetData(int value)
  9.         {
  10.             return string.Format("You entered: {0}", value);
  11.         }
  12.         public string HelloWorld()
  13.         {
  14.             return "Hello World.";
  15.         }
  16.         public string HelloSomeone(string message)
  17.         {
  18.             return "Hello World and " + message + " too.";
  19.         }
  20.         #endregion
  21.     }
  22. }




As I mentioned, adding the WebGet attribute doesn’t actually have any effect on our ability to call the service via JavaScript unless we have a service behavior that will use the metadata.  In our configuration file we add the AjaxBehavior.  Notice the enableWebScript element in the behavior.  This is logically equivalent to adding the ScriptService attribute to an ASMX service.  The final thing we need to do is to expose an endpoint using the webHttpBinding.



Code Snippet



  1.     <system.serviceModel>
  2.     <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
  3.     <services>
  4.             <service  name="WcfModelService.Model">
  5.                 <endpoint address="" binding="webHttpBinding" contract="WcfModelService.IModel" behaviorConfiguration="AjaxBehavior">
  6.                 </endpoint>
  7.             </service>
  8.         </services>
  9.         <behaviors>
  10.             <endpointBehaviors>
  11.                 <behavior name="AjaxBehavior">
  12.           <enableWebScript/>
  13.                 </behavior>
  14.             </endpointBehaviors>
  15.         </behaviors>
  16.     </system.serviceModel>




All we have left to do now is to call the service from JavaScript.  We will keep things simple and add a ScriptManager with a ServiceReference to the Model service.  Now in JavaScript create the service proxy:

var proxy = new WcfModelService.IModel()

and call an operation:

proxy.HelloSomeone(s2, onSuccess, onFail, null); 



Code Snippet



  1. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WcfTest.aspx.cs" Inherits="WcfModelService.Test" %>
  2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  3. <html xmlns="http://www.w3.org/1999/xhtml">
  4. <head>
  5.     <title>Wcf AJAX Service Client Page</title>
  6.     <script type="text/javascript">
  7.     // This function creates an asynchronous call to the service
  8.     function makeCall(operation){
  9.         var n1 = document.getElementById("num1").value;
  10.         var s2 = document.getElementById("string1").value;
  11.         
  12.         
  13.             // Instantiate a service proxy
  14.         var proxy = new WcfModelService.IModel();
  15.             // Call correct operation on proxy      
  16.             switch(operation){
  17.                 case "Data":
  18.                     proxy.GetData( parseInt(n1),  onSuccess, onFail, null);            
  19.                 break;
  20.                 
  21.                 case "Hello":
  22.                     proxy.HelloWorld( onSuccess, onFail, null);                        
  23.                 break;
  24.                 
  25.                 case "Someone":
  26.                     proxy.HelloSomeone(s2, onSuccess, onFail, null);            
  27.                 break;
  28.                 
  29.             
  30.             }
  31.         
  32.     }
  33.     // This function is called when the result from the service call is received
  34.     function onSuccess(callResult){
  35.         document.getElementById("result").value = callResult;
  36.     }
  37.     // This function is called if the service call fails
  38.     function onFail(){
  39.         document.getElementById("result").value = "Error";
  40.     }
  41.     // ]]>
  42.     </script>
  43.     <style type="text/css">
  44.         #num1
  45.         {
  46.             width: 303px;
  47.         }
  48.         #string1
  49.         {
  50.             width: 317px;
  51.         }
  52.         #result
  53.         {
  54.             width: 333px;
  55.         }
  56.         #btnData
  57.         {
  58.             width: 118px;
  59.         }
  60.         #btnHelloWorld
  61.         {
  62.             width: 99px;
  63.         }
  64.     </style>
  65. </head>
  66. <body>
  67.     <h1>
  68.         Wcf AJAX Service Client Page</h1>
  69.     <p>
  70.         A Number:
  71.         <input type="text" id="num1" /></p>
  72.     <p>
  73.         A String:
  74.         <input type="text" id="string1" /></p>
  75.     <input id="btnData" type="button" onclick="return makeCall('Data');"
  76.         value="Get Data" />
  77.     <input id="btnHelloWorld" type="button" onclick="return makeCall('Hello');"
  78.         value="Hello" />
  79.     <input id="btnHelloSomeone" type="button"
  80.         onclick="return makeCall('Someone');" value="Hello Someone" />&nbsp;
  81.     <p>
  82.       Result:
  83.         <input type="text" id="result" /></p>
  84.     <form id="aForm" action="" runat="server">
  85.     <asp:ScriptManager ID="ScriptManager" runat="server" >
  86.         <Services>
  87.             <asp:ServiceReference Path="~/Model.svc" />
  88.         </Services>
  89.     </asp:ScriptManager>
  90.     </form>
  91. </body>
  92. </html>




Here is an ASMX version of the same service.



Code Snippet



  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.Web.Services;
  6. using System.Web.Script.Services;
  7. namespace AsmxModelService
  8. {
  9.     /// <summary>
  10.     /// Summary description for AsmxModelService
  11.     /// </summary>
  12.     [WebService(Namespace = "WcfModelService")]
  13.     [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  14.     [System.ComponentModel.ToolboxItem(false)]
  15.     // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
  16.     [System.Web.Script.Services.ScriptService]
  17.     public class AsmxModelService : System.Web.Services.WebService
  18.     {
  19.         [WebMethod]
  20.         [ScriptMethod(UseHttpGet = true,ResponseFormat = ResponseFormat.Json)]
  21.         public string GetData(int value)
  22.         {
  23.             return string.Format("You entered: {0}", value);
  24.         }
  25.         
  26.         [WebMethod]
  27.         [ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
  28.         public string HelloWorld()
  29.         {
  30.             return "Hello World";
  31.         }
  32.         [WebMethod]
  33.         [ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
  34.         public string HelloSomeone(string message)
  35.         {
  36.             return "Hello World and " + message + " too.";
  37.         }
  38.     }
  39. }




If you need to secure the call, we could force the user to login before accessing the aspx page containing the JavaScript service call.  An Authentication cookie will now be passed to the application with each request. 

 

By setting aspNetCompatibilityEnabled=true we can access the identity using a call to HttpContext.Current.User.Identity.Name.  Each operation should now verify the user is authorized to make the call before executing the operation logic.

Reference:

http://msdn.microsoft.com/en-us/magazine/cc793961.aspx#id0070025

Saturday, August 15, 2009

Extraction Rules

Adding an Extraction Rule

Right click the request and select Add Extraction Rule…

image 

Fill out the properties in the popup. And select Ok.

image

Using an Extracted parameter

You can use an extracted parameter in requests following the request where the parameter was extracted. You can do several things with this extracted value for example add a query string variable to a request or add a form post parameter:

image

image

Sunday, August 9, 2009

Creating an ASMX web service web test with VSTS 2008

Right click on the test project and select Add > Web Test…

IE will be opened with the Web Test Recorder available on the left side.

Click the stop button.

Right Click on the webtest node and select Add Web Service Request.clip_image002

Fill out the properties in the properties window.image

If you include the full path to the web method under test, the web method query variable will be extracted and created for you.

(example: http://localhost/VSTSLoadTest/VstsLoadTestWebService.asmx?op=ReverseTheString).image

Assemble the soap request (I suggest using Internet Explorer to browse the web service and capture a sample soap request.

image

Drill down into the web request to fill out the String Body properties.

image

Set the content type to text/XML.

Click the ellipses button on the string body.

Paste the soap request into the popup – make sure and modify the placeholders with actual values.

image

Monday, August 3, 2009

Staying current

As an Intertech consultant I am asked to do all kinds of things. Architect applications, code in C#, code in VB.Net, write SSRS reports, write TFS Team build test scripts, work with VSTS GDR database scripts the list goes on and on and that suites me just fine. Jumping around and doing so many different things is a challenge and once I get up the learning curve on something I don’t want to lose it. Here are a couple things I do to keep my skills sharp.

I recently earned my MCPD Enterprise Application Developer on the 3.5 framework. Even if you don’t think much of certifications, the real value for me was reading all of the 4500 or so pages of the Microsoft training kits and doing a couple hundred exercises. I was introduced to some things I would never have considered had I not gone through this process. Another great way to stay sharp is going to local users groups.

I’m a big fan of VSTS Test Edition but that sort of work only comes along every couple months. To stay sharp in this area I have gotten active on the Web test and load test forums (TimJim is my display name; you will also find me on the General Architecture and WCF forums). I try to answer a question that has gone unanswered for a while every day or so. It keeps me sharp and it is a great way to help the user community. The user base seems to be relatively small right now and this is a tool I don’t want to see go away. I was blown away by the preview I got of the 2010 version and I expect the user base to grow substantially with that release. I also keep my VSTS skills sharp by frequenting the VSTS Minnesota users group.

Finally, I have a massive book collection. I try to read 4 or 5 technical books a year but good old classroom education is hard to beat. Back in my days as a manager I always budgeted some training dollars but we only seemed to actually see the inside of a classroom every few years. At Intertech I am required (yes required!) to get 40 hours of training per year. A couple weeks ago I took Intertech’s WPF class and went through our Silverlight training material. I had the opportunity to do some Silverlight work right away. I was delighted with how well the class prepared me for the actual work I had to do and I definitely hit the ground running. I also have to say I was pleasantly surprised to find out how feature rich Silverlight is. I’m sure I’ll be blogging on these subjects soon.

Sunday, August 2, 2009

More VSTS 2008 Team Test Edition Tips and tricks.

It has been about a month since I posted any VSTS Test edition tips or tricks. Here is a few I think are particularly important.

BEST PRACTICE: Can we copy/paste (and rename) web tests if we want to? What effect does this have on the data source? Is it advisable from a source control standpoint?

Doing a copy, paste, or rename operation is totally fine. The data source will have to be added again to copied tests. Consider using extract web test rather than copy. Extracting a webtest is a way of grouping a request or requests into another callable web test. This is a very powerful feature. If you make 3 copies of a web test and the test has to change, you will have to modify all three copies of that test. Rather than doing a copy paste, use the extract web test functionality.

  • Go to the test editor window
  • Select the first request you want to include in the test
  • Right click and select Extract Web Test…
  • Enter a meaningful name in the text box
  • The top drop down will contain the first request to include in the test
  • Use the second drop down to select the final request you want to include in the test
  • Check both check boxes at the bottom of the dialog
  • Select OK

In the Test Editor you will see that the requests that you extracted are removed and replaced with a single request that points to another web test.

Now if you need to change that test you change it in one single place. This test may also be included in any other web test by right clicking in the test editor and selecting Insert Call to Web Test…

Can a web test record pop-ups?

If you are having difficulty recording pop-ups using the web recorder, try using Fiddler to capture the HTTP traffic. Once you are done executing your test and capturing the http traffic in fiddler,

BEST PRACTICE: Use validation levels

During a standalone web test you want to run all validations but during a load test you want to only run the most critical validations so you can leave the client or agents to do the work they are intended to do – load the server. Execution of validation rules in a load test has impact on performance.

The default validation rule level of a request is “high”. When you create a load test you can specify what level of validation rules you want to execute under the Run Settings node. The default rule validation level of the load test is “low” therefore if you do nothing with validation levels no validations will be executed during a load test.

If you want to run a validation rule during a load test you want to change the validation level on the most critical rules to ‘‘low’’. In this case low means that it is a low-level, or fundamental, verification; low does not refer to the priority of the verification. Setting the level to ‘‘high’’ means that it will only be run if the load test has been set to a validation level of ‘‘high’’.

Load test levelValidations that will run
LowLow
MediumLow, Medium
HighLow, Medium, High