Recommended

Skip Navigation LinksHome / Articles / View Article

Introducing to Halcyone - Silverlight Application Framework: Silverlight Rest Extensions

+ Add to SilverlightShow Favorites
2 comments   /   posted by Alexey Zakharov on Apr 19, 2010
(4 votes)
Tags: Halcyone , C# , Alexey Zakharov

1. Introduction

In this article I’m going to introduce a first public component of our silverlight application framework called Halcyone. In this experimental component we provide simple way for rapid silverlight REST services development. We are using convection based approach to minimize boilerplate code and provide reactive api based on IObservable push collections introduced in dev labs reactive extensions.

You can get sources and samples from the project codeplex site:

http://halcyone.codeplex.com/

2. Content

2.1 Server side

On the server side of this framework we are using ASP.NET MVC 2. We assume that each controller marked with RestServiceAttribute is a rest service and each public method in it is its rest method. We also dropped uri pattern support and assume that every input parameter is transferred via query string with a keys equal to action parameter names.

Here is the sample rest service based on our convention.

[RestService]
public class FooServiceController : Controller
{
    [HttpPost]
    public ActionResult OperatoionWithPrimitiveInputParameter(int param)
    {
        // Some logic
 
        return this.Json(123);
    }
}

It could be access via following link: http://%7byour_host%7d/FooService/OperationWithPrimitiveInputParameter/?param=134

How it works?

At the web application startup our helper class extract all controllers marked with rest service attribute and register routes using following convention above mentioned convention:

Controller name –> Service name & Action name –> Service Operation

To initiate registration you should specify type of any rest service controller, after that RestControllerRoutesExtracter will extract all other rest service controllers from the same assembly. This approach was borrowed from Fluent Nhibernate mapping configuration.

Here is the sample code for rest service registration:

protected void Application_Start()
{
    var routes = RestControllerRoutesExtracter.Extract<FooServiceController>();
    routes.Run(r => RouteTable.Routes.Add(r));
}

But RestServiceAttribute is not used only as marker of rest services. It it is also use to intercept execution of action. During interception it pass all query string parameters to action parameters which has the same name. If input parameter has array or complex type it tries to deserialize string with json serializer. Also it handles input parameter passed via http request input stream.

2.2 Client side (Silverlight)

On the client side we user the same convention. Name of service client class except postfix “client” is name of web service that would be called. The name of public method is the name of service operation that would be called. All parameter names should be equal to names in the service contract.

While developing API for REST service client we take in mind four requirements:

  1. It should have reactive API. By reactive API I mean provide result as IObservable push collection.
  2. It shouldn’t be based on code generated proxies.
  3. It should be type safe.
  4. It should require minimal code to implement.

Look at the client method for the rest web service, which we created in previous section:

public class FooServiceClient
{
    [HttpPost]
    public IObservable<int> OperatoionWithPrimitiveInputParameter(int param)
    {
        return Rest.GenerateOperation<int, int>(this.OperatoionWithPrimitiveInputParameter)(param);
    }
}

Confused?! Don’t worry it is not so terrible as it seems from the first sight =)

GenerateOperation method parses method info of OperatoionWithPrimitiveInputParameter and produces a strongly typed func that will return an observable result from the rest web service. In our case we have one int input parameter and one int output parameter so function will have following type: Func<int,IObservable<int>>.

To use created rest web service client you should subscribe on exposed observable result.

[TestMethod]
[Asynchronous]
public void OperatoionWithOperatoionWithPrimitiveInputParameter()
{
    var restService = new FooServiceClient();
    IDisposable result = restService.OperatoionWithPrimitiveInputParameter(5)
        .Subscribe
        (
            r =>
            {
                Assert.AreEqual(r, 123);
                this.EnqueueTestComplete();
            }
        );
}

You could also user another rx combinators to make more complex processing. But you should keep in mind that each observable result would call web services only once after first subscription. So if you need perform new request to the web service invoke client method once again and get new observable result.

By default observable result is observed on background thread, that is why if you want to pass data to the UI you should observe result on dispatcher. It could be done using ObserveOnDispatcher method.

Here is the sample code of requesting list of products from server. LoadProducts method returns IObservable<IEnumerable<ProductDto>>.

public ObservableCollection<ProductViewModel> Products = new ObservableCollection<ProductViewModel>();
 
private LoadProducts()
{
    restService.LoadProducts()
    .ObserveOnDispatcher()
    .Subscribe(ps => 
        {
            ps.Do(ConvertToProductViewModel) // converts each product dto to view model
            .Run(p => Products.Add(p)) // add product viewmodels to observable collection
        };
}

2.3 Arrays and complex types

To support arrays and complex type we are using JSON serialization. So if one of the input parameter has array type the rest request will look like this:

http://%7byour_host%7d/FooService/OperatoionWithArrayParameter/?param=[1,3,423,3,5]

Here is the sample service side and client side methods:

[HttpPost]
public ActionResult OperatoionWithArrayParameter(IEnumerable<int> param)
{
    // Some logic
    return this.Json(123);
}
 
[HttpPost]
public IObservable<int> OperatoionWithArrayParameter(IEnumerable<int> param)
{
    return Rest.GenerateOperation<IEnumerable<int>, int>(this.OperatoionWithArrayParameter)(param);
}

Complex parameters works in the same way.

http://localhost:4317/FooService/OperatoionWithComplexInputParameter/?param={'”Name”:”Apple”,”Price”:300}

public class FooParameter
{
    public string Name { get; set; }
 
    public int Price { get; set; }
}
 
[HttpPost]
public ActionResult OperatoionWithComplexInputParameter(FooParameter param)
{
// Some logic
return this.Json(123);
}
 
[HttpPost]
public IObservable<int> OperatoionWithComplexInputParameter(FooParameter param)
{
return Rest.GenerateOperation<FooParameter, int>(this.OperatoionWithComplexInputParameter)(param);
}

2.3 Pass input parameters via Request stream

If you want to pass one of the input parameter via http request input stream just put RequestParameterAttribute to the method parameter:

[HttpPost]
public ActionResult OperatoionWithComplexInputParameterSendedViaRequestStream([RequestParameter] FooParameter param)
{
    // Some logic
    return this.Json(123);
}
 
[HttpPost]
public IObservable<int> OperatoionWithComplexInputParameterSendedViaRequestStream([RequestParameter] FooParameter param)
{
    return Rest.GenerateOperation<FooParameter, int>(this.OperatoionWithComplexInputParameterSendedViaRequestStream)(param);
}

Due to we are using request stream, web request link won’t have any query string parameter:

http://localhost:4317/FooService/OperatoionWithComplexInputParameterSendedViaRequestStream/

2.4 Optional parameters

For optional parameters we are using optional parameter feature of .NET framework 4. Just specify default value in action. After that you no longer need to optionalParam. With the help of optional parameters you can make you services contracts much more compact without creating several overloads.

[HttpPost]
public ActionResult OperatoionWithOptionalParameter(int param, double optionalParam = 0.0)
{
// Some logic
return this.Json(123);
}

3. Summary

We the help of halcyone rest extensions you can create silverlight rest web services much faster.

Halcyone rest extensions has following advantages over WCF web services:

  1. WCF REST services doesn’ support strongly typed input parameters. All parameters are strings.
  2. WCF REST doesn’t provide anything to simplify rest service client development.
  3. ASP.NET MVC extensibility model is much more simpler than WCF.
  4. REST Services are better cached with internet information server (IIS)
  5. REST Services are easy to debug.
  6. WCF require you to create complex configuration sections and produce useless *.svc files. Mistakes in spelling of strings inside these configurations is indictable during compilation.

Rest extensions is the first public component of our silverlight application framework. In near future we are planning to expose more components (Flexible navigation framework, MVVM conventions etc), So track the updates on codeplex project site and my blog

I hope you have enjoyed the article. If you have any questions or you have found a bug, please write a comment or e-mail me through my blog contact form or create a discussion on codeplex.

4. Links

Here is some useful links, which was used while preparing of this article:

1. Halcyone – an application framework for Silverlight

2. Reactive extensions for .NET and Silverlight dev labs page

3. Reactive extensions wiki – if you are not familiar with RX linq operator you should check this resource. It provides a helpful collection of samples in the spirit of 101 LINQ Samples.

4. Fiddler – the best tool for debugging of rest web services

5. Optional Parameters and Named Arguments in C# 4 (and a cool scenario with ASP.NET MVC 2)

Share


Comments

Comments RSS RSS
  • RE: Introducing to Halcyone - Silverlight Application Framework: Silverlight Rest Extensions  

    posted by Adron on Apr 21, 2010 02:35
    Just curious if you have support for providing basic http authentication per the REST requests?
  • RE: Introducing to Halcyone - Silverlight Application Framework: Silverlight Rest Extensions  

    posted by lexer on Apr 21, 2010 16:32
    Out of the box you can use ASP.NET MVC Authorization attribute for this task.

Add Comment

 
 

   
  
  
   
Please add 1 and 3 and type the answer here:

Help us make SilverlightShow even better and win a free t-shirt. Whether you'd like to suggest a change in the structure, content organization, section layout or any other aspect of SilverlightShow appearance - we'd love to hear from you! Need a material (article, tutorial, or other) on a specific topic? Let us know and SilverlightShow content authors will work to have that prepared for you. (hide this)