HTTP/1.1 + REST libraries
As described in the library types page, the HTTP/1.1 + REST libraries are API-specific libraries with source code in the google-api-dotnet-client repository, generated from Discovery documents instead of API protobuf representations. API calls are made over HTTP/1.1 with JSON content.
These libraries depend heavily on the support libraries of Google.Apis and Google.Apis.Core, which have their source code in the same repository.
These libraries are still supported and automatically updated as the API surfaces change, but the underlying code and generator is not being actively worked on, and only high-impact feature requests are considered.
Some features are not as discoverable as we'd like them to be; this page describes the use of these features.
Service object reuse
Service client classes (e.g. DriveService
) are thread-safe and reusable.
Typically your application should create a single instance and use it for all operations.
By default, every instantiation of a service client class creates a new instance of HttpClient, and creating many of these is, in general, a bad idea. See, for example, this post for why.
Sometimes it is required to create an instance per request. In this case make sure
your code calls Dispose
on each instance when it has finished with it.
This will call Dispose
on the HttpClient
.
GoogleWebAuthorizationBroker
GoogleWebAuthorizationBroker requires interaction with the end-user, so can only be used in client-side code.
OAuth authentication requires the end-user to interact with a web browser, which can only be done client-side.
It is possible to use OAuth authentication for end users in a web application, but not using
GoogleWebAuthorizationBroker
. For these purposes, you can use one of the ASP.NET integration packages
we provide, such as Google.Apis.Auth.AspNetCore3
.
You can find general information about Google APIs and OAuth 2.0 in Using OAuth 2.0 to Access Google APIs and about integration with .NET client libraries in the .NET guide for OAuth 2.0 and Google APIs.
Specifying a retry policy
By default, clients will only retry requests which result in an HTTP 503 response. These are retried with exponential backoff, starting with 250ms and doubling on each retry, for a maximum of 3 attempts. Retry policies can be configured on a per-client basis, as shown below:
using Google.Apis.Drive.v3;
using Google.Apis.Http;
using Google.Apis.Util;
using System.Net;
BackOffHandler handler = new BackOffHandler(new BackOffHandler.Initializer(new ExponentialBackOff())
{
HandleUnsuccessfulResponseFunc = response => response.StatusCode == HttpStatusCode.InternalServerError,
HandleExceptionFunc = exception => exception is HttpRequestException
});
DriveService service = new DriveService(new Google.Apis.Services.BaseClientService.Initializer
{
// Prevent the default retry handling from also retrying failures.
DefaultExponentialBackOffPolicy = ExponentialBackOffPolicy.None,
...
});
service.HttpClient.MessageHandler.NumTries = 5;
service.HttpClient.MessageHandler.AddExceptionHandler(handler);
service.HttpClient.MessageHandler.AddUnsuccessfulResponseHandler(handler);
Integration with IHttpClientFactory
For historical reasons, there are two (incompatible) IHttpClientFactory
interfaces:
Google.Apis.Http.IHttpClientFactory
in the REST support librarySystem.Net.Http.IHttpClientFactory
in .NET
The .NET interface is widely used, and supported in the community with projects such as Polly. Unfortunately, the REST support library predates this interface and it can't be directly retro-fitted without breaking changes.
The HttpClientFromMessageHandlerFactory
provides a bridge between the two interfaces. Integration is still
relatively complex, but the integration test for this class
serves as a living example.
GoogleApiHttpBody properties
Some libraries (e.g. Cloud Machine Learning Engine) require un-typed
JSON to be passed through to the server, ignoring the strongly-typed
generated code. This un-typed JSON is represented via the GoogleApiHttpBody
type, usually for a property called HttpBody
.
Setting the data in this property directly does not work as
expected. The workaround is to use the service HttpClient
directly. For example:
DemoService service = new DemoService(...);
// The JSON we want in our HTTP request body
string json = "{ \"property\": 5 }";
// Create an empty body part for the request
DemoRequestBody emptyBody = new DemoRequestBody { HttpBody = new GoogleApiHttpBody() };
// Create the service request as normal
DemoRequest demoRequest = service.Demo(emptyBody);
// Prepare to send the request using the service HttpClient directly
Uri uri = demoRequest.CreateRequest().RequestUri;
HttpContent content = new StringContent(json, Encoding.UTF8, "application/json");
// Execute the request
HttpResponseMessage response = await service.HttpClient.PostAsync(uri, content);
// Check the response status-code, to see if the request completed successfully.
// If so, then JSON response data can now be read directly from the response
response.EnsureSuccessStatusCode();
See issue #1068 for more details.
DateTime/DateTimeOffset properties
Resource properties specified with the "date-time" format in Discovery document are generated as three
properties in the C# code. For a property named timestamp
in the Discovery document,
the generated properties are:
public string TimestampRaw { get; set; }
[Obsolete]
public DateTime Timestamp { get; set; }
public DateTimeOffset TimestampDateTimeOffset { get; set; }
The three properties are all implemented in terms of the "raw" property, which is just a plain string. When fetching the properties with types DateTime
or DateTimeOffset
, the raw string is parsed to the appropriate type. When setting the properties, the raw string is obtained by formatting the value.
The DateTime
-based property is obsolete as its parsing code has some undesirable behavior, including
conversion to local time whereas the underlying value is always expressed in UTC. This behavior can't be
changed without breaking backward compatibility, so instead the DateTimeOffset
-based property was
introduced. We recommend migrating code using the DateTime
-based property to the new property to avoid
this behavior. Likewise, code using the raw string property in order to work around the DateTime
behavior
can now migrate to use the new property (avoiding code clutter) - but it's fine to continue using the raw
property for other reasons, such as to log the precise payload.
Object/DateTimeOffset properties
Resource properties specified with the "google-datetime" format in Discovery document are also generated as three properties in the C# code. For a property named timestamp
in the Discovery document,
the generated properties are:
public string TimestampRaw { get; set; }
[Obsolete]
public object Timestamp { get; set; }
public DateTimeOffset TimestampDateTimeOffset { get; set; }
This situation is more complex than the "date-time" scenario above, as one property has a type of just
object
, preventing proxying all properties through just the raw string property.
The generated code maintains backward compatibility with early versions which only contained the
object
-typed property as far as possible, but we recommend that all existing code using that property
now migrates to use the DateTimeOffset
-based property if possible, or the raw string property otherwise.
Creating a DelegatingHandler from a credential
There are cases where you may wish to create an HttpClient
(potentially with IHttpClientFactory
)
which applies the authentication headers from a Google credential.
This can be performed using the ToDelegatingHandler
extension method targeting the
Google.Apis.Http.IHttpExecuteInterceptor
interface implemented by the credential classes.
This creates a DelegatingHandler
which can be used either in the construction of an HttpClient
, or via the
AddHttpMessageHandler
extension method for dependency injection.
Sample code:
GoogleCredential = GoogleCredential.FromFile(...); // Or any other way of creating a credential
DelegatingHandler handler = credential.ToDelegatingHandler(new HttpClientHandler());
HttpClient client = new HttpClient(handler);
// Requests made by the client will automatically have authorization headers applied.