<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://footheory.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Search results matching tags 'C#' and 'AJAX'</title><link>http://footheory.com/search/SearchResults.aspx?o=DateDescending&amp;tag=C%23,AJAX&amp;orTags=0</link><description>Search results matching tags 'C#' and 'AJAX'</description><dc:language>en-US</dc:language><generator>CommunityServer 2007 SP2 (Debug Build: 20611.960)</generator><item><title>Exposing ASP.NET Page Methods as AJAX Web Services</title><link>http://footheory.com/blogs/bennie/archive/2006/12/01/exposing-asp-net-page-methods-as-ajax-web-services.aspx</link><pubDate>Fri, 01 Dec 2006 23:48:52 GMT</pubDate><guid isPermaLink="false">9ce7e6ef-4587-4f0e-939d-3f75f3a8ddfc:51</guid><dc:creator>bennie</dc:creator><description>&lt;h4&gt;Overview&lt;/h4&gt; &lt;p&gt;In my &lt;a href="http://asiqs.com/blogs/bennie/archive/2006/11/28/Auditing-ASP.Net-pages-with-Ajax.Net-Beta-2.aspx"&gt;previous&lt;/a&gt; post, I showed how you can expose an .asmx web service to AJAX JavaScript code running on the client. In addition to .asmx Web Services, AJAX can also consume 'standard' methods in .ASPX Web Forms. This allows the programmer to directly leverage the functionality implemented in the code-behind page, without the overhead of a full page postback.&lt;/p&gt; &lt;p&gt;In this example, we will create a simulated stock quote application. The application will allow the user to enter a stock symbol, and click a 'Retrieve Quote' button. The client script called by the button will invoke a method in the code-behind of the Web Page, which returns the current price of the stock to the caller.&lt;/p&gt; &lt;h4&gt;Implementation Walkthrough&lt;/h4&gt; &lt;p&gt;First, we need to create a new Web Application Project (or, if you are so inclined, a new 'Web Site' project). For this example, I called the project 'AjaxStockQuote'. After the project is created, we again need to copy the ASP.Net AJAX &lt;font face="Courier New" size="2"&gt;Microsoft.Web.Extensions&lt;/font&gt; assembly to the &lt;font face="Courier New" size="2"&gt;/bin&lt;/font&gt; directory, and set a reference to it. For details please refer to my &lt;a href="http://asiqs.com/blogs/bennie/archive/2006/11/28/Auditing-ASP.Net-pages-with-Ajax.Net-Beta-2.aspx"&gt;previous&lt;/a&gt; post.&lt;/p&gt; &lt;p&gt;Again, we need to make the same web.config changes as&amp;nbsp;we did for the &lt;a href="http://asiqs.com/blogs/bennie/archive/2006/11/28/Auditing-ASP.Net-pages-with-Ajax.Net-Beta-2.aspx"&gt;AjaxAuditing&lt;/a&gt; project&lt;/p&gt; &lt;p&gt;Next, we add a new Web Form named &lt;font face="Courier New" size="2"&gt;StockQuote.aspx&lt;/font&gt; to the project. This form will contain our simple user interface, and the code behind file for this page&amp;nbsp;will contain our method that we will call directly from the client. &lt;/p&gt; &lt;p&gt;In order to be callable from the client, a page method needs to meet the following two conditions:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;It needs to be a &lt;font face="Courier New" size="2"&gt;public, static&lt;/font&gt; method  &lt;li&gt;It needs to be decorated with the &lt;font face="Courier New" size="2"&gt;[WebMethod]&lt;/font&gt; attribute. Because of this, we need to make sure that we include a using statement for &lt;font face="Courier New" size="2"&gt;System.Web.Services&lt;/font&gt;.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;The method itself is very simple. We use a random generator to return a stock price, regardless of the ticker symbol passed-in. But, if you would set a breakpoint in the method, you would notice that the method is being called with the correct parameter. The complete code for&lt;font face="Courier New" size="2"&gt; StockQuote.aspx.cs&lt;/font&gt; is shown below:&lt;/p&gt; &lt;p&gt;&lt;a title="CodeBehind" href="http://www.flickr.com/photos/99548241@N00/311386573/"&gt;&lt;img alt="CodeBehind" src="http://static.flickr.com/104/311386573_4bcdd22e0f.jpg" border="0"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;The Html for the UI of our file is shown below:&lt;/p&gt; &lt;p&gt;&lt;a title="UIHtmlCode" href="http://www.flickr.com/photos/99548241@N00/311386581/"&gt;&lt;img alt="UIHtmlCode" src="http://static.flickr.com/100/311386581_6d13c73583.jpg" border="0"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Our button invokes the &lt;font face="Courier New" size="2"&gt;CallWebService()&lt;/font&gt; JavaScript method, which will invoke our page method. We also have a &lt;font face="Courier New" size="2"&gt;&amp;lt;div&amp;gt;&lt;/font&gt; element with Id &lt;font face="courier " size="2"&gt;'idResult'&lt;/font&gt;. The contents (&lt;font face="courier n" size="2"&gt;innerText&lt;/font&gt;) of this div will be set with the result of our page method invocation.&lt;/p&gt; &lt;p&gt;As before, we need to make sure that we include an &lt;font face="Courier New" size="2"&gt;&amp;lt;asp:ScriptManager&amp;gt;&lt;/font&gt; element as the first element after the &lt;font face="Courier New" size="2"&gt;&amp;lt;form&amp;gt;&lt;/font&gt; tag. This time, we do not &lt;font face="Courier New" size="2"&gt;&amp;lt;ScriptReference&amp;gt;&lt;/font&gt; sub-element, since we are not calling a Web Service, but rather on of our 'own' page methods:&lt;/p&gt; &lt;p&gt;&lt;a title="ScriptManager" href="http://www.flickr.com/photos/99548241@N00/311386579/"&gt;&lt;img alt="ScriptManager" src="http://static.flickr.com/118/311386579_094eefd329.jpg" border="0"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Next, let's take a look at the &lt;font face="Courier New" size="2"&gt;CallWebService()&lt;/font&gt; client JavaScript. Whenever you expose one or more page methods with the &lt;font face="Courier New" size="2"&gt;[WebMethod]&lt;/font&gt; attribute, the ASP.Net AJAX will create a static PageMethods variable, which can be used to invoke the exposed methods as follows:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;PageMethods.&amp;lt;Method Name&amp;gt;([&amp;lt;method param(s)&amp;gt;], [&amp;lt;methodToInvokeUponCompletion&amp;gt;]);&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Where:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Courier New" size="2"&gt;&amp;lt;Method Name&amp;gt;&lt;/font&gt; is the name of the method to be invoked.  &lt;li&gt;&lt;font face="Courier New" size="2"&gt;&amp;lt;method Params&amp;gt; &lt;/font&gt;are the parameters to the method, separated by commas  &lt;li&gt;&lt;font face="Courier New" size="2"&gt;&amp;lt;methodToInvokeUponCompletion&amp;gt;:&lt;/font&gt; This method will be invoked upon completion of the invoked method. The return value of the invoked method will be passed in as the argument to this completion method.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;The full implementation is shown below:&lt;/p&gt; &lt;p&gt;&lt;a title="CallWebService" href="http://www.flickr.com/photos/99548241@N00/311386571/"&gt;&lt;img alt="CallWebService" src="http://static.flickr.com/109/311386571_4a5143fef6.jpg" border="0"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;The implementation uses standard DOM methods to extract and set Html values.&lt;/p&gt; &lt;p&gt;That is really all there is to it. When you run the page and enter a quote, the result area will show the result as shown below:&lt;/p&gt; &lt;p&gt;&lt;a title="RunningPage" href="http://www.flickr.com/photos/99548241@N00/311386576/"&gt;&lt;img alt="RunningPage" src="http://static.flickr.com/109/311386576_10678f0289.jpg" border="0"&gt;&lt;/a&gt;&lt;/p&gt; &lt;div class="wlWriterSmartContent" id="0767317B-992E-4b12-91E0-4F059A8CECA8:008536c5-d1d7-43e4-a8a5-cc72fb05465f" style="padding-right:0px;display:inline;padding-left:0px;padding-bottom:0px;margin:0px;padding-top:0px;"&gt;Technorati tags: &lt;a href="http://technorati.com/tags/ASP.NET" rel="tag"&gt;ASP.NET&lt;/a&gt;, &lt;a href="http://technorati.com/tags/AJAX" rel="tag"&gt;AJAX&lt;/a&gt;, &lt;a href="http://technorati.com/tags/C#" rel="tag"&gt;C#&lt;/a&gt;, &lt;a href="http://technorati.com/tags/.NET%20Framework" rel="tag"&gt;.NET Framework&lt;/a&gt;, &lt;a href="http://technorati.com/tags/Visual%20Studio" rel="tag"&gt;Visual Studio&lt;/a&gt;&lt;/div&gt;</description></item><item><title>Auditing ASP.NET pages with ASP.NET AJAX Beta 2</title><link>http://footheory.com/blogs/bennie/archive/2006/11/28/auditing-asp-net-pages-with-asp-net-ajax-beta-2.aspx</link><pubDate>Wed, 29 Nov 2006 05:43:06 GMT</pubDate><guid isPermaLink="false">9ce7e6ef-4587-4f0e-939d-3f75f3a8ddfc:50</guid><dc:creator>bennie</dc:creator><description>&lt;h4&gt;Problem Description &amp;amp; Requirements&lt;/h4&gt; &lt;p&gt;As part of a&amp;nbsp;recent e-Commerce project&amp;nbsp;I was&amp;nbsp;asked to provide an auditing trail for certain critical application processes, such as:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;The signup of a new member.  &lt;li&gt;A product purchase.  &lt;li&gt;The agreement of a user to terms and conditions.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Typically, such requirements have been addressed by saving the data associated with the transaction, adding a timestamp and the corresponding user id etc. While these measures are definitely appropriate and needed, it would be beneficial&amp;nbsp;if in addition to the this information we could create a permanent record of the&amp;nbsp;web page, as it was shown to the user. This record would contain:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;All of&amp;nbsp;the page's&amp;nbsp;HTML elements (text boxes, radio buttons etc), with the content as filled in by the user.  &lt;li&gt;All of the images that are part of the page.  &lt;li&gt;The exact look &amp;amp; feel of the page, including any CSS styling.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Any solution should have a minimal impact on the existing flow of the application, and should require as little as possible development effort for every page that needs to be audited. Also, the run-time overhead of the solution should be minimal.&lt;/p&gt; &lt;h4&gt;Requirements Analysis&lt;/h4&gt; &lt;p&gt;We looked at a number of ways to implement this, including the application of a HttpModule to capture the Html that was sent back to the browser. At the end, it was concluded that we really should capture the Html that was shown to the user, since this is ultimately a true representation of what the user saw at the time of the transaction.&lt;/p&gt; &lt;p&gt;An Ajax-based solution seems like a very natural fit for this problem. Using Ajax technology, we have the ability to asynchronously call Web Services directly from the client. So, we simply need to get the contents of the Html document, and pass this content to the Web Service. The Web Service can then archive this content in some type of persistent storage, such as a the file system or a relational database. &lt;/p&gt; &lt;p&gt;To keep our example simple, we will save the content of the Html Pages in the file system of the Web Server, where the base name of the created file is the URL of the page. We will append the current time in ticks to base name to create a unique file name.&lt;/p&gt; &lt;p&gt;We will use the &lt;a href="http://ajax.asp.net/" target="_blank"&gt;beta 2 version of Ajax.Net&lt;/a&gt; as our Ajax implementation. Beta 2 shipped a couple of weeks ago, hot on the heels of Beta 1. The CTP version of this technology was also known as Microsoft ATLAS.&lt;/p&gt; &lt;p&gt;ASP.Net AJAX allows the developer to create browser-neutral pages with a rich, responsive UI and and an asynchronous communication model. The framework is tightly integrated with ASP.NET, and this integration will be improved even more by the time &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=82243606-d16d-445c-8949-9ee8c10cda2e&amp;amp;DisplayLang=en" target="_blank"&gt;Orcas&lt;/a&gt; (the next version of Visual Studio) will ship sometime next year. You can download Beta 2 from &lt;a href="http://ajax.asp.net/default.aspx?tabid=47&amp;amp;subtabid=471" target="_blank"&gt;this&lt;/a&gt; location.&lt;/p&gt; &lt;h4&gt;Solution &lt;/h4&gt; &lt;h5&gt;Create the Web Application Project&lt;/h5&gt; &lt;p&gt;To implement this solution, we will first create an ASP.NET Web Application. Make sure that you have the &lt;a href="http://msdn2.microsoft.com/en-us/asp.net/aa336618.aspx" target="_blank"&gt;Web Application project model&lt;/a&gt; installed. Alternatively, you can use a standard "Web Site" project. I have gotten to really like the&amp;nbsp;Web Application Project model, and I'm currently using it for most of my web projects.&lt;/p&gt; &lt;p&gt;In this case, we will call our project "AjaxAuditing", as is shown below:&lt;/p&gt; &lt;p&gt;&lt;img src="http://static.flickr.com/103/309117726_025f5e0532.jpg?v=0"&gt; &lt;/p&gt; &lt;h5&gt;Configure the Project for ASP.Net AJAX&lt;/h5&gt; &lt;p&gt;After the project has been created, we first should go ahead and configure our application for ASP.Net AJAX. When you install Beta 2, a sample &lt;font face="Courier New" size="2"&gt;web.config&lt;/font&gt; file will be provided for you in the following directory:&lt;/p&gt; &lt;p align="center"&gt;&lt;font face="Courier New" size="2"&gt;C:\Program Files\Microsoft ASP.NET\ASP.NET 2.0 AJAX Extensions\v1.0.61025&lt;/font&gt;&lt;/p&gt; &lt;p&gt;You can either copy the entire contents of this file, or you can "lift out" those elements that are required for your project.&amp;nbsp;In our case,&amp;nbsp;we only need scripting and Web Service support. This implies that we need the following configuration elements:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;A &lt;font face="Courier New" size="2"&gt;&amp;lt;sectionGroup&amp;gt;&lt;/font&gt; element in the &lt;font face="Courier New" size="2"&gt;&amp;lt;configsections&amp;gt;&lt;/font&gt; outer element, which configures the scripting component of ASP.Net AJAX.  &lt;li&gt;A &lt;font face="Courier New" size="2"&gt;&amp;lt;httpHandlers&amp;gt;&lt;/font&gt; element to configure Web Service support.  &lt;li&gt;A &lt;font face="Courier New" size="2"&gt;&amp;lt;httpModules&amp;gt;&lt;/font&gt; element to configure the AJAX Script Module.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;For a complete listing of the&lt;font face="Courier New" size="2"&gt; web.config&lt;/font&gt; file of our sample project, I refer the reader&amp;nbsp;to the sample code for this project. &lt;/p&gt; &lt;p&gt;We also need to add a reference to the &lt;font face="Courier New" size="2"&gt;'Microsoft.Web.Extensions.dll'&lt;/font&gt; assembly. This assembly will be located in the program files folder named above. Copy this file into your &lt;font face="Courier New" size="2"&gt;/bin&lt;/font&gt; directory, and add a reference to it, as shown below:&lt;/p&gt; &lt;p&gt;&lt;img src="http://static.flickr.com/116/309116740_b9f04acfe6.jpg?v=0"&gt; &lt;/p&gt; &lt;p&gt;Note that the &lt;font face="Courier New" size="2"&gt;'Microsoft.Web.Extensions'&lt;/font&gt; assembly is installed in the GAC, we are simply referencing it here for build purposes. If you don't like running from the GAC, you can set the "copy local" attribute for this assembly to true, and you will be running with a local copy.&lt;/p&gt; &lt;h5&gt;Add a Web Service to the Project&lt;/h5&gt; &lt;p&gt;Next, we will add the Web Service which will save our Html content to the file system to our project. To do this, follow these steps:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Since we want to separate our Web Services from our main .ASPX code, we will add a new folder named &lt;font face="Courier New" size="2"&gt;'WebServices'&lt;/font&gt; to our project.  &lt;li&gt;To this folder, we add our Web Service, named &lt;font face="Courier New" size="2"&gt;'SaveHtml.asmx'&lt;/font&gt;, as show below:&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&lt;img src="http://static.flickr.com/118/309116747_c638cc670b.jpg?v=0"&gt; &lt;/p&gt; &lt;p&gt;In our code, we need a using statement for the Microsoft.Web.Script.Services assembly:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;using Microsoft.Web.Script.Services;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;We also need to add the '&lt;font face="Courier New" size="2"&gt;ScriptService&lt;/font&gt;' attribute to our Web Service. The ScriptService attribute indicates that we want our Web Service to be callable from client-side JavaScript, and will enable ASP.Net AJAX to dynamically generate a client-side proxy for the Web Service:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;[ScriptService]&lt;br&gt;public class SaveHtml : System.Web.Services.WebService&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font size="2"&gt;The complete code for the Web Service is shown below:&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;img src="http://static.flickr.com/118/309118103_a9e703424e.jpg?v=0"&gt; &lt;/p&gt; &lt;p&gt;Our Web Method takes two parameters:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;The URL of the page. This URL is used to build the first part of the base name of the created file.  &lt;li&gt;The html content itself.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;We create a unique file name by concatenating the current time in ticks, map this file into our web context and write the contents of the Html to the file. &lt;/p&gt; &lt;h5&gt;Create our Test Web Page&lt;/h5&gt; &lt;p&gt;Next, we will enable our &lt;font face="Courier New" size="2"&gt;Default.aspx&lt;/font&gt; Web Page to call our Web Service, using ASP.Net AJAX as follows:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;First, we need to add a register statement for the &lt;font face="Courier New" size="2"&gt;Microsoft.Web.Extensions&lt;/font&gt; assembly. This will import the AJAX asp extensions, which we need to add a reference to the Script Manager (see next).&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;img src="http://static.flickr.com/106/309117730_7a92963ad0.jpg?v=0"&gt; &lt;/p&gt; &lt;ul&gt; &lt;li&gt;Next, we add&amp;nbsp;an &lt;font face="Courier New" size="2"&gt;&amp;lt;asp:ScriptManager&amp;gt;&lt;/font&gt; element&amp;nbsp;to the page. The ScriptManager should always be located in the &lt;font face="Courier New" size="2"&gt;&amp;lt;form&amp;gt;&lt;/font&gt; tag, and should be the first element after the form element. Inside the ScriptManager element, we add a reference to our Web Service by specifying it's relative path:&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;img src="http://static.flickr.com/116/309117734_93518e1f2f.jpg?v=0"&gt; &lt;/p&gt; &lt;ul&gt; &lt;li&gt;In the body of the form, we will add some HTML and ASP elements, such as a TextBox and an image. This will allow us the test our application with some actual Html content. In the 'btnSubmit' button, we add an '&lt;font face="Courier New" size="2"&gt;OnClientClick'&lt;/font&gt; attribute, which invokes a &lt;font face="Courier New" size="2"&gt;'CallSaveHtml()'&lt;/font&gt; JavaScript method. This method will make the actual call to the Web Service (see next section for more info). Note that this button also has a &lt;font face="Courier New" size="2"&gt;OnClick&lt;/font&gt; postback handler, which remains unaffected, so we are not altering the normal application flow.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;img src="http://static.flickr.com/103/309128925_24ed5d17a1.jpg?v=0"&gt; &lt;/p&gt; &lt;ul&gt; &lt;li&gt;Next, we add a JavaScript block to our &amp;lt;head&amp;gt; section, and provide the implementation of our &lt;font face="Courier New" size="2"&gt;CallSaveHtml()&lt;/font&gt; JavaScript method, as shown below:&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;img src="http://static.flickr.com/110/309116753_dec46e0d62.jpg?v=0"&gt; &lt;/p&gt; &lt;p&gt;In the &lt;font face="Courier New" size="2"&gt;CallSaveHtml()&lt;/font&gt; method, we invoke our Web Service by specifying&amp;nbsp;the fully-qualified name of our Web Service method, passing in the following parameters:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;The name of our page (in this case Default.aspx).  &lt;li&gt;The html content of our page.  &lt;li&gt;The final argument is NOT an argument of our web service, but rather is the name of the method, which should be called by the ASP.Net AJAX framework when the web service call completes. Remember, by default all AJAX calls are made asynchronously. In this case, we specify the name of the &lt;font face="Courier New" size="2"&gt;'SaveCompleted()&lt;/font&gt;' method, which displays a 'Save Completed' message in the status bar of the browser.&lt;/li&gt;&lt;/ol&gt; &lt;h5&gt;Testing our Page&lt;/h5&gt; &lt;p&gt;Before we run our page, we need to create an "Auditing" directory in our project. This is the directory which will contain our audit files. Now, we are finally ready to test our page. When we run the page:&lt;/p&gt; &lt;p&gt;&lt;img src="http://static.flickr.com/110/309117732_8fd8e9d48d.jpg?v=0"&gt; &lt;/p&gt; &lt;p&gt;and click the "Submit" button, you will notice that a file is created in the "Auditing" sub-directory:&lt;/p&gt; &lt;p&gt;&lt;img src="http://static.flickr.com/121/309117727_4b206b7b1f.jpg?v=0"&gt; &lt;/p&gt; &lt;p&gt;Navigating to this page in the browser will show an exact replica of our original page, including the image and the text in our TextBox, producing the exact audit as specified in our requirements:&lt;/p&gt; &lt;p&gt;&lt;img src="http://static.flickr.com/100/309116749_5df41f4904.jpg?v=0"&gt; &lt;/p&gt; &lt;p&gt;Also, because we added the call to the &lt;font face="Courier New" size="2"&gt;'CallSaveHtml'&lt;/font&gt; in the &lt;font face="Courier New" size="2"&gt;'OnClientClick'&lt;/font&gt; attribute of our submit button, we preserve the standard postback behavior of the button, so we are not affecting the standard flow of the ASP.NET application, which was another one of our requirements.&lt;/p&gt; &lt;h5&gt;Enhancements&lt;/h5&gt; &lt;p&gt;While our implementation of the &lt;font face="Courier New" size="2"&gt;'CallSaveHtml'&lt;/font&gt; Javascript method will work, it does have a number of drawbacks:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;It has the name of our Page hard-coded as the first argument to the web service call.  &lt;li&gt;We need to add a Javascript block with both of our methods to each web page that we want to audit.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;A better solution would be the following:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Move the generation of the JavaScript code into the code-behind of the page, and use &lt;font face="Courier New" size="2"&gt;Page.ClientScript.RegisterStartupScript&lt;/font&gt; to register the code. This code can dynamically generate the URL of the page, so that hard-coding will no longer be necessary.  &lt;li&gt;Move this code into a base class, from which every page with auditing requirements can inherit.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;For this purpose, I created a &lt;font face="Courier New" size="2"&gt;AuditablePage&lt;/font&gt; base class, which overrides the &lt;font face="Courier New" size="2"&gt;OnLoad()&lt;/font&gt; method. In the &lt;font face="Courier New" size="2"&gt;OnLoad()&lt;/font&gt; method, we use a string builder to create our JavaScript, and we use &lt;font face="Courier New" size="2"&gt;Page.ClientScript.RegisterStartupScript()&lt;/font&gt; to register the script, as is shown below:&lt;/p&gt; &lt;p&gt;&lt;img src="http://static.flickr.com/106/309116748_fd3ef96716.jpg?v=0"&gt; &lt;/p&gt; &lt;p&gt;Note that we use &lt;font face="Courier New" size="2"&gt;Request.Url.AbsolutePath&lt;/font&gt; to get the Url of the page, thereby avoiding the hard-coding we had to do previously.&lt;/p&gt; &lt;p&gt;Now, any page which needs auditing can simply inherit from this base class, as shown in the class diagram below:&lt;/p&gt; &lt;p&gt;&lt;img src="http://static.flickr.com/116/309116754_48345b2060.jpg?v=0"&gt; &lt;/p&gt; &lt;p&gt;Note that these additional pages will still need the ScriptManager, and the call to the&amp;nbsp;&lt;font face="Courier New" size="2"&gt;'CallSaveHtml()&lt;/font&gt;' JavaScript function in the appropriate location, but otherwise, they should be ready to go.&lt;/p&gt; &lt;h5&gt;Real-World Application&lt;/h5&gt; &lt;p&gt;In a real-world application, you would probably not use the file system to store your page audit. You would probably want to store the audit pages in a relational database, and add additional context information such as:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;The name of the current user  &lt;li&gt;The current date &amp;amp; time&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;/p&gt; &lt;div class="wlWriterSmartContent" id="0767317B-992E-4b12-91E0-4F059A8CECA8:08ce697b-cb0f-4f45-ac2a-ffa2dd66d6d3" style="padding-right:0px;display:inline;padding-left:0px;padding-bottom:0px;margin:0px;padding-top:0px;"&gt;Technorati tags: &lt;a href="http://technorati.com/tags/ASP.NET" rel="tag"&gt;ASP.NET&lt;/a&gt;, &lt;a href="http://technorati.com/tags/AJAX" rel="tag"&gt;AJAX&lt;/a&gt;, &lt;a href="http://technorati.com/tags/Auditing" rel="tag"&gt;Auditing&lt;/a&gt;, &lt;a href="http://technorati.com/tags/C#" rel="tag"&gt;C#&lt;/a&gt;, &lt;a href="http://technorati.com/tags/.NET%20Framework" rel="tag"&gt;.NET Framework&lt;/a&gt;&lt;/div&gt;</description></item></channel></rss>