Announcing the New RingCentral SDK for .NET

Tyler Long
Mar 5 · 9 min read

We just released a new RingCentral SDK for .NET to NuGet here:

https://www.nuget.org/packages/RingCentral.Net/

It is currently in beta and will become the new official SDK for all .NET platforms, including the traditional .NET framework for Windows and the latest .NET Core for cross platform applications. There are so many improvements and changes (most of them are backward compatible) that I think it deserves a blog article.

If you want to try the new SDK right now, please go ahead directly to read the RingCentral SDK for .NET upgrade guide.

What’s New in the RingCentral.NET SDK?

  1. The new name is RingCentral.NET
  2. .NET Standard 2.0 for cross platform
  3. Separate packages for PubNub support
  4. Better exception reporting
  5. Namespaced classes to avoid naming collision
  6. Extensive comments
  7. Fewer dependencies
  8. Support all API request content types
  9. Better SCIM API support
  10. No more background timers to refresh tokens
  11. No more objects of anonymous types as parameters
  12. Support multiple query parameters with the same key
  13. Almost all code is now auto-generated
  14. New AfterHttpCall event
  15. Expose low level API
  16. Sample code for all the endpoints

The new name is RingCentral.Net

Before the release of the new SDK, the recommended RingCentral SDK for C# developers is this one: https://github.com/ringcentral/ringcentral-csharp-client

I never liked the name ringcentral-csharp-client. It is kind of long. And “client” often indicates apps instead of libraries. The name RingCentral.Net is way better: https://github.com/ringcentral/RingCentral.Net

.NET Standard 2.0 for cross platform

It is important for us to support the .NET community across all implementations and with a single, consistent package. We believe .NET Standard is the future of .NET community for this standardization. Having a single package targeting multiple platforms is just too good to refuse.

Earlier Portable Class Library (PCL) was used and we even implemented it in our first C# SDK, but it’s never really got things correct and was difficult to implement.

.NET Standard is for sharing code. .NET Standard is a set of APIs that all .NET implementations must provide to conform to the standard. This unifies the .NET implementations and prevents future fragmentation. It replaces Portable Class Libraries (PCLs) as the tool for building .NET libraries that work everywhere.

Read more about .NET Standard and support across various .NET implementations here:

https://docs.microsoft.com/en-us/dotnet/standard/net-standard

Separate packages for PubNub support

The RingCentral API supports two types of push notifications, webhooks and PubNub. Earlier versions of our C# SDK included PubNub support directly within the core SDK. However, not all RingCentral SDK users need PubNub and the PubNub SDK is a little heavyweight, so it makes more sense to move PubNub to two separate packages:

Both of these are designed to work with the RingCentral.Net SDK.

Better exception reporting

With the old SDK, we often get support cases from developers reporting that things don’t work as expected. Developers would encounter exceptions, but they would not know what the issue is because the SDK doesn’t tell them directly what is wrong.

The old way

We realized that it is partially because ringcentral-csharp-client has terrible exception reporting mechanism. Whenever there is an HTTP exception, what user gets is a FlurlHttpException as ringcentral-csharp-client depends on https://flurl.io/). But FlurlHttpException itself at the first glance is rather meaningless because it tells you nothing more than “something is wrong”. In order to get “what is wrong”, developers need to write code:

try
{
...
}
catch (FlurlHttpException fhe)
{
string errorMessage = fhe.GetResponseString();
Console.WriteLine(errorMessage);
if (fhe.Call.Response.StatusCode ==
System.Net.HttpStatusCode.NotFound)
{
Console.WriteLine("The resource doesn't exist");
}
}

The key point is: developer has to write code to explicitly read the response body out. What if they also need the request body, request headers and response headers? They need to write more code.

The new way

With the new SDK, things have changed a lot. Developers don’t even need to try catch in order to know what is wrong. Because whenever there is an HTTP exception, a RestException will be thrown, whose exception message contains every detail about the HTTP request, including the request and response body, headers, etc.

Below is an exception thrown by the new RingCentral.Net SDK. I didn’t even catch it, and my IDE shows the error message directly:

With the information above, I clearly know what is wrong and I also clearly know how are the request and response like. Did I write any code to extract the information above? Nope, it’s not necessary. The SDK prepares the exception message for me.

Namespaced classes to avoid naming collision

We create classes for things in Restful API. Let’s take call log for example:

/restapi/v1.0/account/{accountId}/call-log/restapi/v1.0/account/{accountId}/extension/{extensionId}/call-log

There are two call-log endpoints and, in this case, this call-log could be either account level call log or extension level call log. And fortunately no matter which level it is, their data structure is the same, so we don’t need to distinguish them.

But there are cases that there are naming collisions, for example:

/restapi/v1.0/account/{accountId}/service-info/restapi/v1.0/account/{accountId}/extension/{extensionId}/meeting/service-info

There are two service-info endpoints. One is for account while the other is for meeting. They are completely two different things so that we need to create two different classes. Problem is we cannot have two classes with the same name, unless they are in different namespaces.

Previously, we used a hacked way to solve the problem, we made exceptions and renamed the second service-info to meeting-service-info. It’s not an ideal approach because it might cause confusion to people. We prefer a set of pre-defined rules to follow with ideally no exceptions.

In the latest change, we put things that might name-collide into different namespaces. So that they can have their original names without any collision.

Extensive comments

RingCentral’s API endpoints are growing. We offer more and more features via our RESTful API. Both we and our developers face a challenge: where can we find information about the features? Especially when there are lots of request parameters,

Let’s take the TokenRequest class for example:

With smart IDEs, developers can navigate to the classes and read comments with ease.

Compared to the old versions of SDK, we provide more information via comments:

  • Enum: possible values for a property
  • Maximum: maximum value for a property
  • Minimum: minimum value for a property
  • Default: default value for a property
  • Required: whether this property is required

With extensive comments, developers can get almost all the information they need while developing, without leaving their IDEs.

Fewer dependencies

Now the library only depends on Newtonsoft.Json if you don’t need PubNub subscriptions.

Previously the SDK also depended on Flurl (https://flurl.io/), which depends on a couple of other libraries. We still think Flurl is a good library, but we decided to move away from it for two reasons:

  • It has never gotten really popular. 3 years after we adopted it, it has only 1.5K stars on GitHub. It’s not a great idea to depend on something not overly popular because it might discontinued at any time.
  • The fewer dependencies, the better. We realized that we could get everything done without Flurl and any other HTTP client libraries.

Support all API request content types

We added support for all the content type consumed by RingCentral server:

  • multipart/mixed
  • multipart/form-data
  • text/*
  • application/json
  • application/x-www-form-urlencoded

Previously we didn’t support text/* and multipart/mixed properly because they are either rarely used or pretty new. multipart/mixed is only used to upload a new greeting audio. text/* it is only needed when you want to update the text of a Glip post.

Here is a complete test cases for dealing with multipart/mixed data, by the way.

Better SCIM API support

Previous version didn’t have tests for the SCIM API. In the new version we wrote extensive test suites for SCIM API.

The SCIM API is special and hard to support because it has special property names, such as :

urn:ietf:params:scim:schemas:extension:enterprise:2.0:User

Most programming languages doesn’t support special characters in property names. We fixed it by defining JsonProperty mapping:

[JsonProperty("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User")]
public EnterpriseUser urn_ietf_params_scim_schemas_extension_enterprise_2_0_User;

We now have a complete test suite for SCIM API support.

No more background timers to refresh tokens

Previously, the SDK defaulted to start background timers to refresh tokens. It’s too magical. Some developers want to manage tokens themselves and it’s annoying that the tokens got refreshed without their explicit instructions.

Automatic refresh might be useful in some quick and dirty prototype projects, but it is not a reliable way to keep the token alive, because, for each token, you need to have a RestClient instance running 24x7, otherwise there is no timer to refresh it. Developers need to write token management code anyway for serious applications so it’s better to just get rid of the auto refresh magic.

There are several solutions for token refresh, please choose one based on your requirements:

  • If your app is simple and each run won’t last longer than an hour. You don’t need to bother refreshing token because a new access token is valid for as long as an hour.
  • For some simple apps, it is also viable to do authorization to get new token for every run, especially for private app password flow. Getting a new token for every run, then there is no need to refresh an existing token.
  • Create a background timer (or use system provided cron job solution) to do token refresh. You can set the timer to run every half hour so that you always have a valid access token.
  • Refresh on demand by catching token expired exceptions. Whenever you get such an exception then you know it’s time to refresh token.
  • If you call API very infrequently, you can just do a refresh explicitly before each API call. Or you can check the validness of token before each API call to decide whether you should refresh the token or not. A reliable way to determine token validness is to issue a real API call.

No more objects of anonymous types as parameters

C# is a typed language and its full power cannot be unleashed if we defined all the parameters as anonymous types instead of specific types.

For example, in previous version, the following code is allowed:

var queryParams = new {
type = new string[]{"Voice"},
view = "Detailed",
withRecording = true,
perPage = 10
}

Here, queryParams is an anonymous type. We are using C# as if it is a dynamic language so the compiler cannot help us much.

In the latest version of SDK, you need to explicitly specify the type of a query Parameter. Since it is a typed class, the compiler will tell us if there is a typo in its properties.

Support multiple query parameters with the same key

var queryParams = {
type = new[] { "Sms", "Fax" }
}

Will be translated to ?type=Sms&type=Fax, which is recognized by RingCentral server side.

Almost all code is now auto-generated

Except for some sugar and utility code. The code is generated based on Swagger / OpenAPI 2.0 spec. As long as future APIs follow supported code generation, as RingCentral releases more APIs, we should be able to easily support them in SDK by re-generating code.

New AfterHttpCall event

By listening to this event, you will get notified after each HTTP call. And you will get all the details about that HTTP call, including request & response, body & headers.

So if you want to build some kind of middleware, such as auto rate limit threshold, AfterHttpCall is a good place to start. For more information, please reference this test case.

Expose low level API

We tried out best to make the SDK developer friendly and take advantage of C#’s types. But if you want to use low level HTTP API, we give you the flexibility as well:

var responseMessage = await
rc.Get("/restapi/v1.0/dictionary/country/46");
Assert.Equal(HttpStatusCode.OK, responseMessage.StatusCode);

From the example above, you can see that you can specify the API endpoint directly by specifying a string. And you get a HttpResponseMessage object back which you can use to inspect everything the server returns.

Sample code for all the endpoints

Together with this new SDK, we provide sample code for all the endpoints. So matter which API endpoint you want to invoke, you can always find code snippet to reference.

Summary

We have made a lot of improvements to RingCentral .NET SDK and we recommend you to try it now. Please go ahead directly to read the RingCentral SDK for .NET upgrade guide.

RingCentral Developers

Cloud Business Communications

Thanks to Vyshakh Babji.

Tyler Long

Written by

RingCentral Developers

Cloud Business Communications