Saturday, September 21, 2013

BaseClientService parameters

All Google API services inherit from BaseClientService. This class and ClientServiceRequest contain the main logic of all API calls no matter which API it is (Translate, Drive, Storage, Analytics, etc.).
BaseClientService is a thread-safe base service class and its initializer contains several properties which can change the default behavior. In this blogpost I'll go through the different properties, their default values and how you can set them differently.

I miss you Thailand!

Let's start:

HttpClientFactory
/// <summary>
/// Gets or sets the factory for creating HttpClient instance. If this
/// property is not set, the service uses a new HttpClientFactory instance.
/// </summary>
public IHttpClientFactory HttpClientFactory { get; set; }

Our recommendation is to leave this property with its default value which is null. As a result, a new HttpClientFactory will be created automatically for you. Two reason why you may consider to set this property:
1. You want to create a single ConfigurableHttpClient instance and share it with more than one service. In that case you may want to cache the created ConfigurableHttpClient. A potential solution may look like this:

public class SingleInstanceHttpClientFactory : IHttpClientFactory
   {
       private ConfigurableHttpClient configurableHttpClient;
       private object lockObject = new object();


       public ConfigurableHttpClient CreateHttpClient(CreateHttpClientArgs args)
       {
           if (configurableHttpClient == null)
           {
               lock (lockObject)
               {
                   // double lock
                   if (configurableHttpClient == null)
                   {
                       configurableHttpClient = new HttpClientFactory().CreateHttpClient(args);
                   }
               }
           }
           return configurableHttpClient;
       }
   }

2. You want to create or configure the ConfigurableHttpClient differently. Take a look in our MockHttpClientFactory for an example of Mocking the factory. In that implementation the factory doesn't create HttpClientHandler (which is the component that actually creates the wire calls, and uses HttpWebResponse and HttpWebRequest internally).

HttpClientInitializer
/// <summary>
/// Gets or sets an HTTP client initializer which is able to customize properties on ConfigurableHttpClient 
/// and ConfigurableMessageHandler.
/// </summary>
public IConfigurableHttpClientInitializer HttpClientInitializer { get; set; }

This property allow you to customize the behavior of the ConfigurableHttpClient. if you want to set different properties in ConfigurableHttpClient, like NumTries, or even common HttpClient properties like Timeout or DefaultRequestHeaders you can do so with your implementation of IConfigurableHttpClientInitializer interface.. 
The following initializer is a simple implementation that changes the timeout to 10 seconds and NumTries to 5 maximum tries for each HTTP request:

public class MyInitializer : IConfigurableHttpClientInitializer
   {
       public void Initialize(ConfigurableHttpClient httpClient)
       {
           httpClient.Timeout = TimeSpan.FromSeconds(10);
httpClient.MessageHandler.NumTries = 5;
       }
   }

Another future usage (in the future 1.6.0-beta release) is to implement a Credential class as IConfigurableHttpClientInitializer. It will add itself to the list of interceptors for adding the "Authorization" header to a HTTP request, and also to the unsuccessful response handlers list to refresh the token in case we got 401 (Unauthorized response).

DefaultExponentialBackOffPolicy
/// <summary>
/// Get or sets the exponential back-off policy used by the service. Default value is
/// <c>UnsuccessfulResponse503</c>, which means that exponential back-off is used on 503 abnormal HTTP
/// response.
/// If the value is set to <c>None</c>, no exponential back-off policy is used, and it's up to user to
/// configure the ConfigurableMessageHandler using IConfigurableHttpClientInitializer to set a specific 
/// back-off implementation (using BackOffHandler).
/// </summary>
public ExponentialBackOffPolicy DefaultExponentialBackOffPolicy { get; set; }

Exponential back-off is enabled by default to retry 503 responses. If you want to implement your own back-off policy you can change this value to None and create your own implementation of back-off. Here is a sample code for doing so:


/// Creating the client service initializer with none exponential back-off policy and set the initializer
/// to my back-off initializer implementation.
   var initializer = new BaseClientService.Initializer()
   {
       ApplicationName = "My Back-off sample",
       DefaultExponentialBackOffPolicy = ExponentialBackOffPolicy.None,
       HttpClientInitializer = new MyBackOffInitializer()
   };
   var service = new BooksService(initializer);


   public class MyBackOffInitializer : IConfigurableHttpClientInitializer
   {
       /// <summary>
       /// My fixed back-off sample implementation. It returns a given fixed number of seconds for 
/// each retry and it will keep retrying for no more than 20 times. Note that the number of tries
/// is honored also by <seealso cref="ConfigurableMessageHandler.NumTries"/>. As a result
/// there are going to be no more than 
/// min(<seealso cref="ConfigurableMessageHandler.NumTries"/>, 
/// <see cref="MaxNumOfRetries"/>+1) tries.
       /// </summary>
       public class MyFixedBackOff : IBackOff
       {
           private readonly int seconds;
           public MyFixedBackOff(int seconds)
           {
               this.seconds = seconds;
           }


           public TimeSpan GetNextBackOff(int currentRetry)
           {
               return TimeSpan.FromSeconds(seconds);
           }


           public int MaxNumOfRetries
           {
               get { return 20; }
           }
       }


       public void Initialize(ConfigurableHttpClient httpClient)
       {
           /// Create a back-off handler with the new fixed back-off logic.
           var backOffHandler = new BackOffHandler(new MyFixedBackOff(2));
           /// Add the back-off handler as unsuccessful response handler.
           httpClient.MessageHandler.UnsuccessfulResponseHandlers.Add(backOffHandler);
       }
   }

You can also consider implementing the Initialzie method as following, and back-off for each 5xx response.


       public void Initialize(ConfigurableHttpClient httpClient)
       {
           /// Create a back-off handler with the new fixed back-off logic, and retry for every 
/// 5xx response.
           var backOffHandler = new BackOffHandler(new BackOffHandler.Initializer(new MyFixedBackOff(2))
               {
/// Support exponential back-off for each 5xx request.
                   HandleUnsuccessfulResponseFunc = (resp) => (int)resp.StatusCode > 500
               });


           /// Add the back-off handler as unsuccessful response handler.
           httpClient.MessageHandler.UnsuccessfulResponseHandlers.Add(backOffHandler);
       }

Take a look at our HttpBackOffHandler and IBackOff classes for more information.

GZipEnabled
/// <summary>Gets or sets whether this service supports GZip. Default value is <c>true</c>.</summary>
public bool GZipEnabled { get; set; }

Simple property to define if this client service instance will use the GZipStream (Currently we use GZipStream from the Zlib.Portable package).

ISerializer
/// <summary>
/// Gets and Sets the Serializer. Default value is NewtonsoftJsonSerializer.
/// </summary>
public ISerializer Serializer { get; set; }

Defines the serializer we use to serialize requests and deserialize responses. Currently we support only JSON serializer, but in the future we want to target more data formats.
ISerializer supports both serialize and deserialize methods and a Format property which is used as the media type ("application/" + Format).

ApiKey
/// <summary> Gets or sets the API Key. Default value is <c>null</c>. </summary>
public string ApiKey { get; set; }

You should set this value when calling an API which doesn't access private data (e.g. Translate API). This key is used to authenticate your application for accounting purposes. More documentation is available here.  
In API Console, after you create a new project and enable a specific API (using the Services tab), the API Key will be available for you in the API Access tab as following:




IAuthenticator
/// <summary>
/// Gets or sets the Authenticator. Default value is NullAuthenticator.Instance.
/// </summary>
public IAuthenticator Authenticator { get; set; }

Note: This property is going to be obsolete from the next version (1.6.0-beta) and it's going to be deleted in version 1.7.0-beta.
IAuthenticator is our current implementation of OAuth2, which we are going to change completely. In the next release we are going to provide a new Google.Apis.Auth NuGet package which will target .NET 4 applications, Windows Phone 8, Windows store apps and PCL!
The current implementation uses IAuthenticator to apply the Authorization header to a service request. From version 1.6.0-beta we will introduce authorization code flow, user's credential and other components. That's a great idea for my next blogpost :)

ApplicationName
/// <summary>
/// Gets or sets Application name to be used in the User-Agent header. Default value is <c>null</c>.
/// </summary>
public string ApplicationName { get; set; }

Base practice is to set this property to your application name. In old versions of the library, if this property was left empty, the library extracted the name from the assembly using reflection. That's not available for us anymore since this solution isn't PCL, so it is highly recommended that you set it manually.

As you can see, you have so many different options to set the different parameters when you construct a new service. We try to provide the best default values so you won't need to customize the default behavior, but if you wish to do so, everything is doable :)

4 comments:

  1. Hi,

    If you have a moment, I need some help. I am currently trying to initiate a service using the new BaseClientService.Initializer, which does NOT include the "Authenticator" parameter. I have my Oauth2 parameters and Token and I simply cannot figure out how to make the Service login. Any advice?

    Thanks for your time!

    Richard

    ReplyDelete
  2. Take a look in one of our samples at https://code.google.com/p/google-api-dotnet-client/source/browse/?repo=samples, and also in my lstest blogposts.

    ReplyDelete
  3. Thank you for the reply. I have spent the last few days poring over those examples and basically what I cannot figure out is this:

    1) I am using the non-Google Windows Forms method to retrieve the toke as explained here: http://www.daimto.com/google-api-and-oath2/

    2) From that I am completely confused as to how to take a Token (in string form) and convert it to a UserCredential so that I don't have to use the GoogleWebAuthorizationBroker.AuthorizeAsync method and have the user authenticate twice.

    Any thoughts?

    And... thanks for ALL of your work on the apis.

    ReplyDelete
    Replies
    1. Sorry for the huge delay... Just came back from Brazil, and it was amazing!!!

      Take a look in the following SO thread: http://stackoverflow.com/questions/21007866/net-google-api-1-7-beta-authenticating-with-refresh-token.
      I think you will find your answer there.

      Let me know,
      Eyal

      Delete