Skip to content

SilverlightServiceClient

mythz edited this page Mar 27, 2013 · 16 revisions

The recommended way to get Silverlight builds of ServiceStack client libraries is from NuGet:

ServiceStack Silverlight Client on NuGet

Or if you prefer not to NuGet you can download the Silverlight client libraries from: https://github.com/ServiceStack/ServiceStack/tree/master/NuGet/ServiceStack.Common/lib/sl5 https://github.com/ServiceStack/ServiceStack.Text/tree/master/NuGet/lib/sl5

These binaries are custom builds of the full ServiceStack Service Clients providing JSON, JSV and XML ServiceClients.

Note: Due to restrictions in Silverlight only the Async operations are supported.


Building Silverlight JSON Service Client from Scratch

Although it is recommended to use the published binaries above, for illustrative purposes, this section below walks through creating a Silverlight client from scratch. This example below shows how to create a JSON ServiceClient.

Note: The code below has been tested in Silverlight 5. It should work fine in other versions of Silverlight as well.

Please be aware of the following considerations:

Uses HttpWebRequest with ClientHttp handling

The Silverlight REST Services Quickstart describes the different mechanisms of how http calls can be made from Silverlight. There is the easier-but-heavier WebClient implementation, and the lighter-but-harder HttpWebRequest implementation. The code below uses the HttpWebRequest implementation, primarily because you cannot manipulate cookies with the WebClient version, but also because it provides better hooks for serializing and deserializing the JSON we want to send to our ServiceStack service.

It also uses the ClientHttp handling instead of BrowserHttp handling. See the differences here.

You may need to put a clientaccesspolicy.xml file in the root of your host. It depends on the domains of the host and client, and the trust level of the particular Silverlight client application (differs between out-of-browser and between Silverlight versions). See MSDN documentation for more info.

Is Cookie-Aware

In the code below, you will see how cookies can be enabled or disabled. Because we are using ClientHttp handling, when cookies are enabled, we have to create a custom CookieContainer.

In order to share these cookies with the browser, we are populating from HtmlPage.Document.Cookies before the request, and then setting the cookies back to the browser after getting a response. This technique allows things like ServiceStack's built-in authentication to work, even if you authenticate in the browser before launching your Silverlight app.

If you don't want to share cookies with the browser, you can set EnableCookies = false, or you can modify the code to persist the CookieContainer in some other way.

Talks only JSON

The request and response streams are assumed to be JSON, and the Accept and Content-Type headers are set accordingly. If you want to implement xml or jsv, you will have to adjust the headers and the content appropriately.

Using the DataContractJsonSerializer

The only JSON serializer that comes with Silverlight is the DataContractJsonSerializer. While not as performant as ServiceStack's serializers, performance on the client usually tends not to be as important as on the server (due to load).

Note that if you decide to apply a [DataContract] attribute to your DTO that you must specifically mark all members that you want to serialize with [DataMember]. If you leave them off, all public members will be serialized.

The code below uses this serializer by default. If you use it, be aware that there is a bug in the DataContractJsonSerializer related to handling of dates that are not of type DateTimeKind.Local. Read more here

Using the ServiceStack JsonSerializer

The Silverlight build of ServiceStack.Text (the one that comes with the ServiceStack.Client.Silverlight) is easy to implement. There is code in the below sample that is commented out, showing how to do this. Simply uncomment the code and remove the DCJS implementation, then reference ServiceStack.Text and you now have a faster serializer (one without the date bug).

Example Usage

Setting up your DTOs

The regular ServiceStack example DTOs should work:

public class Hello
{
    public string Name { get; set; }
}

public class HelloResponse
{
    public string Result { get; set; }
}

Your DTO projects will need to be accessible from both Silverlight and your other .Net projects. There are two ways to accomplish this:

Shared Projects

You can use a set of shared project for porting your DTOs to Silverlight. One should be a regular .Net Class Library (perhaps named MySilverlightApp.DTOs), and another should be a Silverlight Class Library (perhaps named MySilverlightApp.DTOs.Silverlight). You can use the project linking technique documented here.

You may also want to use the Visual Studio Project Linker add-in, which is available here and documented here.

Portable Class Libraries

You can put your DTOs in a single project that is set up as a Portable Class Library. This is a much easier solution, but you will not be able to take on any dependencies.

This means that you cannot use the [RestService] attribute on your DTOs with this approach. Instead, use one of the other ways to register services, such as Routes.Add() in your AppHost.Config.

The RestService Attribute

If you choose to use the [RestService] attribute as shown above, you will find that Silverlight doesn't know anything about it. To overcome this, you can add the following to your shared DTO projects.

If you do not use the [RestService] attribute, but instead use the Routes.Add() method, you do not need this step.

namespace MySilverlightApp.DTOs
{
#if SILVERLIGHT
    public class RestServiceAttribute : System.Attribute
    {
        public RestServiceAttribute(string path) { }

        public RestServiceAttribute(string path, string verbs) { }

        public RestServiceAttribute(string path, string verbs, string defaultContentType) { }
    }
#else
    public class RestServiceAttribute : ServiceStack.ServiceHost.RestServiceAttribute
    {
        public RestServiceAttribute(string path)
            : base(path, null, null) { }

        public RestServiceAttribute(string path, string verbs)
            : base(path, verbs, null) { }

        public RestServiceAttribute(string path, string verbs, string defaultContentType)
            : base(path, verbs, defaultContentType) { }
    }
#endif
}

The conditional if statment will ensure that a dummy attribute is created for Silverlight, while a real attribute that extends ServiceStack's RestServiceAttribute is used on the server-side.

Example Usage

The following shows an example of posting to the HelloService registered at /api/hello using an inline lambda for the response. While this is the simplest usage, you could also use a traditional event handling method, or some other technique. Note that the ServiceClient already makes sure that the Completed event is fired on Silverlight's UI thread.

// create a service client in the current scope
var serviceClient = new ServiceClient<Hello, HelloResponse>();

// decide how to handle the completed event
serviceClient.Completed +=
    (sender, args) =>
        {
            // check for web exceptions
            var webEx = args.Error as WebException;
            if (webEx != null)
            {
                // show a web exception and exit
                var webResponse = (HttpWebResponse)webEx.Response;
                MessageBox.Show(webResponse.StatusDescription);
                return;
            }

            // re-throw any other exceptions
            if (args.Error != null)
                throw args.Error;

            // get the result from the response
            var result = args.Response.Result;

            // do something
            MessageBox.Show(response);
        };

// create a new request
var request = new Hello { Name = "John" };

// post it to the service
serviceClient.Post("/hello", request);

The ServiceClient Implementation

Here is the actual code that implements the ServiceClient. If you've followed all of the above instructions, you should be able to use it easily in your application. Simply change the namespace as you desire.

using System;
using System.Net;
using System.Net.Browser;
using System.Runtime.Serialization.Json;
using System.Windows;
using System.Windows.Browser;
//using ServiceStack.Text;  // uncomment if using SS serializer instead of DCJS.

namespace MySilverlightApp
{
    public class ServiceClient<TRequest, TResponse>
        where TRequest : class
        where TResponse : class
    {
        private readonly string _baseUri;

        public bool EnableCookies { get; set; }

        public ServiceClient(string baseUri = "/api")
        {
            // make sure the base uri is set appropriately
            if (!baseUri.StartsWith("http", StringComparison.InvariantCultureIgnoreCase))
            {
                var source = Application.Current.Host.Source;
                var rootUri = source.AbsoluteUri.Substring(0, source.AbsoluteUri.Length - source.AbsolutePath.Length);
                if (!baseUri.StartsWith("/"))
                    baseUri = "/" + baseUri;
                baseUri = rootUri + baseUri;
            }
            this._baseUri = baseUri;

            // cookies are on by default
            this.EnableCookies = true;
        }

        public void Send(string uri, string method, TRequest data = null)
        {
            // set up the web request
            var webRequest = (HttpWebRequest)WebRequestCreator.ClientHttp.Create(new Uri(_baseUri + uri));
            webRequest.Method = method;

            // if cookies are enabled, pass them in from the browser
            if (this.EnableCookies)
            {
                webRequest.CookieContainer = new CookieContainer();
                webRequest.CookieContainer.SetCookies(new Uri(_baseUri), HtmlPage.Document.Cookies);
            }

            // set the accept header so our response is in json
            webRequest.Accept = "application/json";

            // if we have data to stream, start streaming.  Otherwise we can get the response now.
            if (data != null)
                webRequest.BeginGetRequestStream(RequestCallback, new DataContainer(webRequest, data));
            else
                webRequest.BeginGetResponse(this.ResponseCallback, webRequest);
        }

        private void RequestCallback(IAsyncResult asyncResult)
        {
            try
            {
                // Get the web request stream
                var container = (DataContainer)asyncResult.AsyncState;
                var webRequest = container.WebRequest;
                var stream = webRequest.EndGetRequestStream(asyncResult);

                // set the content type to json
                webRequest.ContentType = "application/json";

                // serialize the object to json and write it to the stream
                var serializer = new DataContractJsonSerializer(typeof(TRequest));
                serializer.WriteObject(stream, container.Data);
                stream.Flush();
                stream.Close();

                // If you want to use ServiceStack's serializer, replace the previous code block with this one.
                //using (var writer = new StreamWriter(stream))
                //{
                //    var serializer = new JsonSerializer<TRequest>();
                //    serializer.SerializeToWriter(container.Data, writer);
                //}


                // now we can get the response
                webRequest.BeginGetResponse(ResponseCallback, webRequest);
            }
            catch (Exception ex)
            {
                // Raise our own event for the error on the UI thread
                var args = new ServiceClientEventArgs<TResponse>(ex);
                Deployment.Current.Dispatcher.BeginInvoke(() => this.OnCompleted(args));
            }

        }

        private void ResponseCallback(IAsyncResult asyncResult)
        {
            try
            {
                // Get the web response
                var webRequest = (HttpWebRequest)asyncResult.AsyncState;
                var webResponse = webRequest.EndGetResponse(asyncResult);

                // Get the web response stream
                var stream = webResponse.GetResponseStream();

                // Deserialize the json data in the response stream
                var serializer = new DataContractJsonSerializer(typeof(TResponse));
                var response = (TResponse)serializer.ReadObject(stream);

                // If you want to use ServiceStack's serializer, replace the previous code block with this one.
                //TResponse response;
                //using (var reader = new StreamReader(stream))
                //{
                //    var serializer = new JsonSerializer<TResponse>();
                //    response = serializer.DeserializeFromReader(reader);
                //}


                // Switch to the UI thread
                var args = new ServiceClientEventArgs<TResponse>(response);
                Deployment.Current.Dispatcher.BeginInvoke(
                    () =>
                    {
                        // if cookies are enabled, pass them back to the browser
                        if (this.EnableCookies && webRequest.CookieContainer != null)
                        {
                            var cookieHeader = webRequest.CookieContainer.GetCookieHeader(new Uri(_baseUri));
                            HtmlPage.Document.Cookies = cookieHeader;
                        }

                        //Raise our own event for the response
                        this.OnCompleted(args);
                    });
            }
            catch (Exception ex)
            {
                // Raise our own event for the error on the UI thread
                var args = new ServiceClientEventArgs<TResponse>(ex);
                Deployment.Current.Dispatcher.BeginInvoke(() => this.OnCompleted(args));
            }
        }

        public void Get(string uri)
        {
            this.Send(uri, "GET");
        }

        public void Post(string uri, TRequest data = null)
        {
            this.Send(uri, "POST", data);
        }

        public void Put(string uri, TRequest data = null)
        {
            this.Send(uri, "PUT", data);
        }

        public void Patch(string uri, TRequest data = null)
        {
            this.Send(uri, "PATCH", data);
        }

        public void Delete(string uri, TRequest data = null)
        {
            this.Send(uri, "DELETE", data);
        }

        public event EventHandler<ServiceClientEventArgs<TResponse>> Completed;

        protected void OnCompleted(ServiceClientEventArgs<TResponse> e)
        {
            var handler = this.Completed;
            if (handler != null)
                handler(this, e);
        }

        private class DataContainer
        {
            public DataContainer(HttpWebRequest webRequest, TRequest data)
            {
                this.WebRequest = webRequest;
                this.Data = data;
            }

            public HttpWebRequest WebRequest { get; private set; }
            public TRequest Data { get; private set; }
        }
    }

    public class ServiceClientEventArgs<TResponse> : EventArgs
        where TResponse : class
    {
        private readonly TResponse _response;
        private readonly Exception _error;

        public ServiceClientEventArgs(TResponse response)
        {
            this._response = response;
        }

        public ServiceClientEventArgs(Exception error)
        {
            this._error = error;
        }

        public TResponse Response
        {
            get { return this._response; }
        }

        public Exception Error
        {
            get { return this._error; }
        }
    }
}

Community Resources



  1. Getting Started

    1. Creating your first project
    2. Create Service from scratch
    3. Your first webservice explained
    4. Example Projects Overview
    5. Learning Resources
  2. Designing APIs

    1. ServiceStack API Design
    2. Designing a REST-ful service with ServiceStack
    3. Simple Customer REST Example
    4. How to design a Message-Based API
    5. Software complexity and role of DTOs
  3. Reference

    1. Order of Operations
    2. The IoC container
    3. Configuration and AppSettings
    4. Metadata page
    5. Rest, SOAP & default endpoints
    6. SOAP support
    7. Routing
    8. Service return types
    9. Customize HTTP Responses
    10. Customize JSON Responses
    11. Plugins
    12. Validation
    13. Error Handling
    14. Security
    15. Debugging
    16. JavaScript Client Library (ss-utils.js)
  4. Clients

    1. Overview
    2. C#/.NET client
      1. .NET Core Clients
    3. Add ServiceStack Reference
      1. C# Add Reference
      2. F# Add Reference
      3. VB.NET Add Reference
      4. Swift Add Reference
      5. Java Add Reference
    4. Silverlight client
    5. JavaScript client
      1. Add TypeScript Reference
    6. Dart Client
    7. MQ Clients
  5. Formats

    1. Overview
    2. JSON/JSV and XML
    3. HTML5 Report Format
    4. CSV Format
    5. MessagePack Format
    6. ProtoBuf Format
  6. View Engines 4. Razor & Markdown Razor

    1. Markdown Razor
  7. Hosts

    1. IIS
    2. Self-hosting
    3. Messaging
    4. Mono
  8. Security

    1. Authentication
    2. Sessions
    3. Restricting Services
    4. Encrypted Messaging
  9. Advanced

    1. Configuration options
    2. Access HTTP specific features in services
    3. Logging
    4. Serialization/deserialization
    5. Request/response filters
    6. Filter attributes
    7. Concurrency Model
    8. Built-in profiling
    9. Form Hijacking Prevention
    10. Auto-Mapping
    11. HTTP Utils
    12. Dump Utils
    13. Virtual File System
    14. Config API
    15. Physical Project Structure
    16. Modularizing Services
    17. MVC Integration
    18. ServiceStack Integration
    19. Embedded Native Desktop Apps
    20. Auto Batched Requests
    21. Versioning
    22. Multitenancy
  10. Caching

  11. Caching Providers

  12. HTTP Caching 1. CacheResponse Attribute 2. Cache Aware Clients

  13. Auto Query

  14. Overview

  15. Why Not OData

  16. AutoQuery RDBMS

  17. AutoQuery Data 1. AutoQuery Memory 2. AutoQuery Service 3. AutoQuery DynamoDB

  18. Server Events

    1. Overview
    2. JavaScript Client
    3. C# Server Events Client
    4. Redis Server Events
  19. Service Gateway

    1. Overview
    2. Service Discovery
  20. Encrypted Messaging

    1. Overview
    2. Encrypted Client
  21. Plugins

    1. Auto Query
    2. Server Sent Events
    3. Swagger API
    4. Postman
    5. Request logger
    6. Sitemaps
    7. Cancellable Requests
    8. CorsFeature
  22. Tests

    1. Testing
    2. HowTo write unit/integration tests
  23. ServiceStackVS

    1. Install ServiceStackVS
    2. Add ServiceStack Reference
    3. TypeScript React Template
    4. React, Redux Chat App
    5. AngularJS App Template
    6. React Desktop Apps
  24. Other Languages

    1. FSharp
      1. Add ServiceStack Reference
    2. VB.NET
      1. Add ServiceStack Reference
    3. Swift
    4. Swift Add Reference
    5. Java
      1. Add ServiceStack Reference
      2. Android Studio & IntelliJ
      3. Eclipse
  25. Amazon Web Services

  26. ServiceStack.Aws

  27. PocoDynamo

  28. AWS Live Demos

  29. Getting Started with AWS

  30. Deployment

    1. Deploy Multiple Sites to single AWS Instance
      1. Simple Deployments to AWS with WebDeploy
    2. Advanced Deployments with OctopusDeploy
  31. Install 3rd Party Products

    1. Redis on Windows
    2. RabbitMQ on Windows
  32. Use Cases

    1. Single Page Apps
    2. HTML, CSS and JS Minifiers
    3. Azure
    4. Connecting to Azure Redis via SSL
    5. Logging
    6. Bundling and Minification
    7. NHibernate
  33. Performance

    1. Real world performance
  34. Other Products

    1. ServiceStack.Redis
    2. ServiceStack.OrmLite
    3. ServiceStack.Text
  35. Future

    1. Roadmap
Clone this wiki locally