Accessing ASP.NET Application Services from Silverlight (part 2)

In part 1, we set ourselves up for success by creating the necessary project structure, xaml file and changes to web.config to enable application services for script access. Oh, and I mentioned you need ASP.NET AJAX on your web server as we'll be needing that JSON serialisation stuff it can do for you.

To access the services from our managed code, we'll be using the BrowserHttpWebRequest class. We don't really have much option - it's the only one we've got right now. I'd point you to some documentation for BrowserHttpWebRequest but I can't find any - suffice to say it's a friend of HttpWebRequest and works in a similar fashion.

We create our BrowserHttpWebRequest object by passing in the URI for our service (we'll come back to that). There are then two pre-requisites for this to work:

  • We need to set the Content-Type header in the request to "application/json; charset=utf-8"
  • We need to set the method (HTTP verb) to "Post"

Then we may or may not send some data in the body of the request via the GetRequestStream() method. So something like:

 BrowserHttpWebRequest request = 
  new BrowserHttpWebRequest(new Uri("Authentication_JSON_AppService.axd/Login"));
request.Headers["Content-Type"] = 
  "application/json; charset=utf-8";
request.Method = "Post";

// Do some stuff with the request body here

HttpWebResponse response =
  request.GetResponse();

...should do the job for us. Hold on a second - where did the URL "Authentication_JSON_AppService.axd/Login" come from? Well I just stepped through AJAX libraries until I got the request.Invoke and checked what the URL was. Alternatively (in retrospect), using Fiddler might have been an easier approach. I did have to mess around for a long time with the help of Fiddler to get the request body correct before I was successful in calling these services. It's an indispensable tool.

So lets tackle the Membership service first. I start off be creating a few constants that will come in handy and a string to store the "base" URL:

 private const string AuthenticationServiceMethod = 
  "Authentication_JSON_AppService.axd/Login";
private const string ContentType = 
  "application/json; charset=utf-8";
private const string HttpMethod = "Post";

private string ServiceUrl;

I have a method called GetServiceUrl that we call from Page_Loaded to initialise ServiceUrl:

 private string GetServiceUrl()
{
  string absUri = HtmlPage.DocumentUri.AbsoluteUri;
  return absUri.Substring(0, absUri.LastIndexOf("/") + 1);
}

In my case this gives me a ServiceUrl of https://localhost:53369/WebService/. I next have a method to create a suitably initialised BrowserHttpWebRequest object:

 private BrowserHttpWebRequest CreateNewApplicationServiceRequest(string ServiceMethod)
{
  BrowserHttpWebRequest request = 
    new BrowserHttpWebRequest(new Uri(ServiceUrl + ServiceMethod));
  request.Headers["Content-Type"] = ContentType;
  request.Method = HttpMethod;
  return request;
}

Then my ExecuteLogin() method can be simplified to something that looks like this:

 private bool ExecuteLogin()
{
  BrowserHttpWebRequest authRequest = 
    CreateNewApplicationServiceRequest(AuthenticationServiceMethod);

  JavaScriptSerializer j_ser = new JavaScriptSerializer();
  AuthenticationRequest ar = new AuthenticationRequest("Mike", "testing123;", false);

  StreamWriter sw = new StreamWriter(authRequest.GetRequestStream());
  sw.Write(j_ser.Serialize(ar));
  sw.Flush();
  HttpWebResponse response = authRequest.GetResponse();
  sw.Close();

  StreamReader responseReader = new StreamReader(response.GetResponseStream());
  string rawResponse = responseReader.ReadToEnd();
  responseReader.Close();
  response.Close();
  return j_ser.Deserialize<bool>(rawResponse);
}

AuthenticationRequest is a little helper class I created (see below). I wanted to use an anonymous type but I ran into problems with the JSON serialiser. userName etc should really be properties but this was just quick and dirty to get it working:

 public class AuthenticationRequest
{
  public string userName;
  public string password;
  public bool createPersistentCookie;

  private AuthenticationRequest()
  {
  }

  public AuthenticationRequest(string userName, string password, bool createPersistentCookie)
  {
    this.userName = userName;
    this.password = password;
    this.createPersistentCookie = createPersistentCookie;
  }
}

I've called the Membership service synchronously in this case (I will do it asynchronously for the Profile properties) and I get back a response which is either true for success (ie authenticated) or false for a failure to authenticate. So in the OnClick event handler I have:

 protected void OnClick(object sender, MouseEventArgs e)
{
  if (sender == TextBlock1)
    TextBlock1.Text = 
      ExecuteLogin() ? "Success! You are logged in." : "Failed. You are not logged in.";
  else
    TextBlock2.Text = "Your shoe size is: " + up.ShoeSize.ToString();
}

So when the first TextBlock is clicked, the ExecuteLogin() method is called and an appropriate message displayed based on the result of that call.

Again this is getting quite long so in part 3 I'll look at accessing the Profile service asynchronously.

Technorati tags: silverlight, asp.net ajax, membership