The Simple Guide to Porting C# Code for RestSharp Next v107 and Beyond

RestSharp made huge changes with the release of v107. It’s a major upgrade, which contains some breaking changes. For instance, most of the interfaces are now gone. For me, it meant all of our legacy API connection code broke without substantial rework if anyone updated NuGet packages in a project.

Personally, I feel there’s a bit of a versioning issue here which is solving and creating technical debt all at the same time. We’ve been using basically the same RestSharp code in production since our vendors moved to REST. This is the first change which has necessitated a major rewrite since we began testing this library when our company first started using C#.

Newer RestSharp requires some substantial adjustments to make everything work as expected with the newer versions. Let’s skip to what used to work, what it should be changed to, and what to take from this.

The Old Way Things Worked

I chose the specific API (I’m NDA’ed to not speak ill of our vendors by name) since it’s the least resilient to industry changes and the most frustrating for the most basic things. If I can make code work for this vendor, I can make it work anywhere.

I’ve had to rewrite this code literally dozens of time because the developers are at best incompetent (at worst, I assume they’re sadists who hate developers). The good news is, this should all read like mostly standard RestSharp code with a few caveats. We actually have multiple versions of this API library with alternatives for weird functions (all undocumented and the vendor pretends it’s normal to need to rewrite RestSharp code different from their own “standard” test code) we need but don’t want available in not-completely-internal production code (if you hadn’t figured it out by now, this API is charitably trash).

Our workflow basically boils down to: instantiate a RestClient, instantiate a RestRequest and method, pass our RequestFormat, pass our headers, pass our body and/or parameters, then finally Execute our request. Anything that appears weird is due to the esoteric manner the API works and some specific RestSharp behaviors.

pre-107 _getToken() method: private string _getToken()
RestClient newclient = new RestClient(url);
RestRequest newrequest = new RestRequest(tokenuri, Method.POST);

string mimetype = "application/x-www-form-urlencoded";
string tokens = "stuff we need for authentication";

newrequest.RequestFormat = DataFormat.Json;
newrequest.AddHeader("content-type", mimetype);

newrequest.AddHeader("accept", "application/json");

newrequest.AddParameter(mimetype, tokens, ParameterType.RequestBody);

IRestResponse newresponse = newclient.Execute(newrequest);

I’ve removed all of our error handling and similar for this code to focus on the RestSharp specific pieces. We basically connect to a url, use our tokenuri variable (defined in the class) for the actual token path, and supply necessary data to authenticate with the specific API.

public string _getApiCall(string apiurl, string apitoken, string method)
RestClient newclient = new RestClient(url);
RestRequest newrequest = new RestRequest(apiurl, Method.GET);

string mimetype = "application/x-www-form-urlencoded";
string tokens = "authentication stuff, we don't need it here technically, but it's easier to have one call";

newrequest.RequestFormat = DataFormat.Json;
newrequest.AddHeader("content-type", mimetype);
newrequest.AddHeader("accept", "application/json");
newrequest.AddHeader("Authorization", "Bearer " + apitoken);

newrequest.AddParameter(mimetype, tokens);

IRestResponse newresponse = newclient.Execute(newrequest);

We use several variables to abstract this. Our apiurl is the specific API method we need, the apitoken is a pregenerated token, and the method is what we’re running for logging purposes (not included). On to the next sample.

private bool _postApiCall(string apiurl, string apitoken, string json, string method)
RestClient newclient = new RestClient(url);
RestRequest newrequest = new RestRequest(apiurl, Method.POST);

string mimetype = "application/json";
string tokens = "same as before";

newrequest.RequestFormat = DataFormat.Json;
newrequest.AddHeader("content-type", mimetype);
newrequest.AddHeader("accept", mimetype);
newrequest.AddHeader("Authorization", "Bearer " + apitoken);

newrequest.AddParameter(mimetype, tokens);
newrequest.AddParameter(mimetype, json, ParameterType.RequestBody);

IRestResponse newresponse = newclient.Execute(newrequest);

This method uses a bool because the API this is written for doesn’t return anything of value aside from success with any of the post calls we use. The json is a pre-generated json for the body. I could theoretically have a json serializer do the lifting for me in this code, but we form some json blobs on the fly.

The New Way of Doing Things

I included the method declarations previously because RestSharp 107 and newer require the code to be written asynchronously. Basically, every non-async method has been deprecated or removed.

private async Task<string> _getToken()
RestClient newclient = new RestClient(url);
RestRequest newrequest = new RestRequest("/api/token", Method.Post);

string mimetype = "application/x-www-form-urlencoded";
string tokens = "stuff to get our token, same as before";

newrequest.RequestFormat = DataFormat.Json;
newrequest.AddHeader("accept", "application/json");
newrequest.AddHeader("content-type", mimetype);

newrequest.AddBody(tokens, mimetype);

RestResponse newresponse = await newclient.ExecuteAsync(newrequest);

Note, the method has been changed from all caps POST to Post (the same happens with get). Also, the newrequest.AddParameter(mimetype, tokens, ParameterType.RequestBody); (this is how the vendor explicitly instructed us to use their API) changed to newrequest.AddBody(tokens, mimetype); in this snippet. Some APIs I tested work fine using a standard AddParameter, but others required a move to using AddBody.

public async Task<string> _getApiCall(string apiurl, string apitoken, string method)
RestClient newclient = new RestClient(url);
RestRequest newrequest = new RestRequest(apiurl, Method.Get);

string mimetype = "application/x-www-form-urlencoded";
string tokens = "same tokens as before";

newrequest.RequestFormat = DataFormat.Json;
newrequest.AddHeader("accept", "application/json");
newrequest.AddHeader("content-type", mimetype);

newrequest.AddHeader("Authorization", "Bearer " + apitoken);

newrequest.AddBody(tokens, mimetype);

RestResponse newresponse = await newclient.ExecuteAsync(newrequest);

The post method is virtually the same as the token method, just different parameters and headers as required by the specific API.

public async Task<bool> _postApiCall(string apiurl, string apitoken, string json, string method)
RestClient newclient = new RestClient(url);
RestRequest newrequest = new RestRequest(apiurl, Method.Post);

string mimetype = "application/json";
string tokens = "same tokens as before";

newrequest.RequestFormat = DataFormat.Json;
newrequest.AddHeader("content-type", mimetype);
newrequest.AddHeader("accept", mimetype);
newrequest.AddHeader("Authorization", "Bearer " + apitoken);

newrequest.AddBody(json, mimetype);

RestResponse newresponse = await newclient.ExecutePostAsync(newrequest);

What Changed

All references to interfaces have been removed. With RestSharp, there really weren’t any alternative class implementations for the interfaces. There isn’t a point of inheritance if there’s no real benefit.

The actual HTTP methods (e.g. Methods.POST) changed case which isn’t a big deal, but takes a little work to replace. You can just find and replace to make it easier, but still, you need to open solutions to do so (or use another solution to bulk regex). I experienced issues with parameters versus the body on certain APIs, but your mileage will vary depending on what you’re using.

The biggest change is the move to asynchronous methods. It takes a lot more effort to go through and add your necessary awaiters, change out method declarations, etc. This was a lot less convenient and requires a lot more effort. We avoided moving a lot of our projects until we were able to unit test adequately.

How to Apply This to Your Code

The changes are relatively simple, but they require some careful planning depending on how and where your code is used. The removal of interfaces and the changes to methods are trivial, but the move to thread safe, asynchronous coding can take a bit more effort. Some frameworks or codebases may require substantial rework.

We staged our migration from pre-107 to 107 and still have projects in the pipeline for our maintenance cycles. Long term, you’re going to want to upgrade to make sure found bugs and exploits don’t impact your production code. Right now though, it’s okay to take some time to clean things up right.

Make sure your code can work asynchronously. While there shouldn’t be any issues with modern versions of C#, legacy code you update may or may not have unexpected edge-cases. Some environments (I’ve mainly experienced this with older .NET framework deployments on client systems) don’t like too many threads running and you can get issues there.

You also need to be aware of the difference between thread aware and thread safe code. Most API work isn’t going to get better with asynchronous programming aside from offloading the heavy lifting from the main thread.

All in all, this migration is relatively easy, but you just need to make sure to change out all the relevant pieces. I had the most difficulty with the code around my API calls and wrapping all of it to be asynchronous.

Image by videaki from Pixabay

Categories: Tech+
Some Dude:

View Comments (2)

  • I am really impressed together with your
    writing skills as neatly as with the structure to your
    weblog. Is that this a paid theme or did you customize it your self?

    Anyway stay up the nice quality writing, it is rare to peer a nice
    blog like this one today..