Silverlight Networking - Getting credentials to work, a new WebRequest

This is already my third article on Silverlight Networking, with only the Credentials in mind. Please read my other articles first:

The workaround

It took me some time to get the work-around ready enough for this article. I must say we finally have the possibility to make a WebRequest with Credentials. Although we have another problem to solve, it's crossdomain scripting! This article explains how you can make use of the Javascript abilities to make a WebRequest.

Using the ASP.NET Ajax Javascript libraries

I know it's not very difficult to build some code around XMLHttpRequest, but there's a very nice wrapper build by the people behind ASP.NET Ajax. This wrapper is the Sys.Net.WebRequest class. This would make a call as simple as the following code:

1 // This function adds and removes the 2 // Web request completed event handler. 3 function WebRequestCompleted() 4 { 5 // Instantiate the WebRequest. 6 var wRequest = new Sys.Net.WebRequest(); 7 8 // Set the request Url. 9 wRequest.set_url(getPage); 10 11 // Set the web request completed event handler, 12 // for processing return data. 13 wRequest.add_completed(OnWebRequestCompleted); 14 alert("Added Web request completed handler"); 15 16 // Remove the web request completed event handler. 17 // Comment the following two lines if you want to 18 // use the handler. 19 wRequest.remove_completed(OnWebRequestCompleted); 20 alert("Removed handler; the Web request return is not processed."); 21 22 // Execute the request. 23 wRequest.invoke(); 24 }

To use this library you first have to download it and reference the MicrosoftAjax.js as follows:

<script type="text/javascript" src="JS/MicrosoftAjax.js"></script>

Calling dynamic Javascript code from Silverlight

It is possible to call Javascript code that already exists by using the HtmlPage.Window.Invoke() method. But as explained next I want to built up the Javascript dynamically.

To call a string containing Javascript you will have to make use of the HtmlPage.Window.Eval() method.

HtmlPage.Window.Eval("(function(){ alert('Hello world! Using dynamic Javascript');})();");

Just for my own sake I build a small wrapper around the Eval function, that just delegates to HtmlPage.Window.

1 public static class JavascriptBridge 2 { 3 private static HtmlWindow window = HtmlPage.Window; 4 5 public static object Eval(string code) 6 { 7 return window.Eval(code); 8 } 9 }

Setting up a JavascriptWebRequest in C#

My idea was to build a C# class that makes use of Javascript to do the actual WebRequest but looks quite similar to the WebRequest object. I came up with the following class which probably speaks for itself.

1 public class JavascriptWebRequest 2 { 3 public string Uri { get; set; } 4 public string Method { get; set; } 5 public Dictionary<string, string> Headers { get; set; } 6 public string Body { get; set; } 7 8 public JavascriptWebRequest() 9 { 10 Headers = new Dictionary<string, string>(); 11 } 12 13 public void Execute() 14 { 15 JavascriptWebRequestExecutor.Execute(this); 16 } 17 }

I for myself really like this class. It allows me to write code like this:

1 var request = new JavascriptWebRequest 2 { 3 Uri = "http://localhost:2851/Test.xml", 4 Headers = 5 { 6 {"Authorization", string.Format("Basic {0}", new NetworkCredential("username","password").ToString().EncodeTo64())} 7 }, 8 Method = "GET" 9 }; 10 request.Execute();

Where's all the magic?

When you look at the JavascriptWebRequest Execute method you see some magic happens behind the scenes. What happens is the JavascriptWebRequestExecutor does some translation of the JavascriptWebRequest properties to a Sys.Net.Webrequest. The request above will translate to the following Javascript.

1 // Instantiate the WebRequest object. 2 var request = new Sys.Net.WebRequest(); 3 4 // Set the request Url. 5 request.set_url('http://localhost:2851/Test.xml'); 6 7 // Set the request verb. 8 request.set_httpVerb('GET'); 9 10 // Set the headers. 11 12 request.get_headers()['Authorization']='Basic dQBzAGUAcgBuAGEAbQBlADoAcABhAHMAcwB3AG8AcgBkAA=='; 13 14 // Set the body 15 request.set_body('');

I posted the sourcecode of the full JavascriptWebRequestExecutor below. It's just a start with lot's of uses of string.Format. I think it's not that readable at this moment, maybe I'll refactor it in the future to some more readable code for the templates.

1 public class JavascriptWebRequestExecutor 2 { 3 public static void Execute(JavascriptWebRequest request) 4 { 5 string variableName = "request"; 6 string javascriptToExecute = string.Format(requestFunctionTemplate, ToJavascriptVariableDeclarion(request, variableName), variableName); 7 JavascriptBridge.Eval(javascriptToExecute); 8 } 9 10 private static string ToJavascriptVariableDeclarion(JavascriptWebRequest request, string variableName) 11 { 12 //Set up the headers 13 IList<string> headers = new List<string>(); 14 foreach (var header in request.Headers) 15 headers.Add(string.Format(headerTemplate, variableName, header.Key, header.Value)); 16 string headersPresentation = string.Join(Environment.NewLine, headers.ToArray()); 17 //Return the complete variable presentation of the request. 18 return string.Format(requestVariableTemplate, variableName, request.Uri, request.Method, headersPresentation, request.Body); 19 } 20 21 22 private const string requestFunctionTemplate = 23 @" 24 (function() 25 {{ 26 {0} 27 {1}.add_completed((function(executor, eventArgs){{ alert(executor.get_responseData()); }})); 28 {1}.invoke(); 29 }})(); 30 "; 31 32 private const string requestVariableTemplate = 33 @" 34 // Instantiate the WebRequest object. 35 var {0} = new Sys.Net.WebRequest(); 36 37 // Set the request Url. 38 {0}.set_url('{1}'); 39 40 // Set the request verb. 41 {0}.set_httpVerb('{2}'); 42 43 // Set the headers. 44 {3} 45 // Set the body 46 {0}.set_body('{4}'); 47 "; 48 49 private const string headerTemplate = 50 @" 51 {0}.get_headers()['{1}']='{2}'; 52 "; 53 }

Conclusions

So we now have a possibility to make a webrequest through javascript. Although this code works as it seems there still is some work to be done. First, we need some way to get the result back, and not only that we also need to get the result back to the rightful caller of the webrequest. More on this in the next post. Besides this, I already mentioned to trouble with crossdomain scripting, this also needs to be covered. If anyone has a good idea on this, leave some comments on my blog.

Gravatar