cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Announcements
Want to learn some quick and useful tips to make your day easier? Check out how Calvin uses Replay to get feedback from other teams at Dropbox here.

Dropbox API Support & Feedback

Find help with the Dropbox API from other developers.

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Re: Exemplary C# Code to Handle Exception Value Retry-After when using the .NET API?

How to get at Retry-After when using the .NET API and using UploadAsync to upload

LinuxCub
Explorer | Level 3

We use the .NET API to upload files using UploadAsync

I recently got an error: System.AggregateException: One or more errors occurred. ---> Dropbox.Api.RateLimitException: too_many_requests/.

 

So, I need to ratelimit, fine, I'm OK with that. Elsewhere there's talk about a Retry-After header that tell you how long to wait before retrying. But how do I get at that from C# ??? So far, I can only guess how long to wait, and that's just silly.

 

There ought to be a way to get at that value, right ?

5 Replies 5

Greg-DB
Dropbox Staff

Yes, you should be able to access that in the RateLimitException.RetryAfter property.

Tech Dev Oldsmar
Helpful | Level 5

Hunting for some exemplary C# (and or best practice advice) on the RateLimitingException but not finding much.  Not sure how to test this but I was thinking:

 

        public static async Task<FolderMetadata> CreateFolder2(DropboxClient client, string path)
        {
            //https://github.com/dropbox/dropbox-sdk-dotnet/blob/main/dropbox-sdk-dotnet/Examples/SimpleTest/Program.cs
            var accountInfo = await client.Users.GetCurrentAccountAsync(); // FOR ACCESSING TEAM FOLDERS
            client = client.WithPathRoot(new PathRoot.Root(accountInfo.RootInfo.RootNamespaceId));
            var folderArg = new CreateFolderArg(path, true);
            try
            {
                var folder = await client.Files.CreateFolderV2Async(folderArg);
                
                return folder.Metadata;
            }
            catch (ApiException<CreateFolderError> e)
            {
                if (e.Message.StartsWith("path/conflict/folder"))
                {
                    Console.WriteLine("Folder already exists... Skipping create");
                    return new FolderMetadata();
                }
                else
                {
                    throw e;
                }
            }
            catch (ApiException<RateLimitException> e)
            {
                Console.WriteLine("Rate Limit Error, try again after " + e.Message +  " seconds. Setting delay");
                Thread.Sleep(Convert.ToInt32(e.ErrorResponse.RetryAfter) * 1000);
                throw e;
            }
        }

Is this in the right ballpark? 

 

Thanks! 

 

 

Greg-DB
Dropbox Staff

@Tech Dev Oldsmar It looks like you have the right idea here; you're grabbing the Retry-After from 'e.ErrorResponse.RetryAfter'. (Exactly how you structure your app with respect to exception messaging and thread handling is up to you though. E.g., it seems like in your sample you wait and then re-throw the exception.) I notice that your 'Console.WriteLine' line right before that seems wrong though; you're plugging in 'e.Message' when it seems like you meant to plug in the number of seconds, which would be 'e.ErrorResponse.RetryAfter' instead. Also I believe you should just do 'catch (RateLimitException e)' instead of 'catch (ApiException<RateLimitException> e)'.

 

Also, in the other exception handling you have, note that we don't recommend doing string comparisons like that. The best practice is to instead use the provided structured error objects, like this:

catch (ApiException<CreateFolderError> e)
{

	// you can write your error handling to be as granular as you wish, using as much or as little of this as you want:
	if (e.ErrorResponse.IsPath) { 
		if (e.ErrorResponse.AsPath.Value.IsConflict)
        {
			if (e.ErrorResponse.AsPath.Value.AsConflict.IsConflict)
            {
				if (e.ErrorResponse.AsPath.Value.AsConflict.Value.IsFolder)
				{
					Console.WriteLine("A folder already exists at this path");
					// whatever handling you need to do
				}
				else if (e.ErrorResponse.AsPath.Value.AsConflict.Value.IsFile)
				{
					Console.WriteLine("A file already exists at this path");
					// whatever handling you need to do
				}
				else if (e.ErrorResponse.AsPath.Value.AsConflict.Value.IsFileAncestor)
				{
					Console.WriteLine("A file already exists at this a parent of this path");
					// whatever handling you need to do
				}
				else if (e.ErrorResponse.AsPath.Value.AsConflict.Value.IsOther) {
					Console.WriteLine("Other error");
					// whatever handling you need to do
				}
			} // else... and so on as desired
		} // else... and so on as desired
	} // else... and so on as desired

 

Tech Dev Oldsmar
Helpful | Level 5

Thank you Greg - very helpful.  BTW, the string comparison that caught your eye is from the DBX .NET C# sample on Github (simpletest.cs).  I thought it was a little odd too but the .NET samples were nevertheless an amazing help.  

 

 

 

Greg-DB
Dropbox Staff

@Tech Dev Oldsmar Thanks for pointing that out! I'll ask the team to improve that example.

Need more support?