/// <summary> /// API post using multipart/form-data. /// </summary> /// <param name="application">The full Uri you want to call (including any get parameters)</param> /// <param name="getParameters">Get parameters (or null if none)</param> /// <param name="postParameters">Post parameters as an object or JObject /// </param> /// <returns>The result as a JObject, with MetaData filled in.</returns> public async Task <JObject> PostFormAsync(string application, object getParameters, object postParameters, params string[] fileParameterNames) { string uri = AddGetParams(makeUri(application), getParameters); using (DisposableCollection objectsToDispose = new DisposableCollection()) { MultipartFormDataContent content = objectsToDispose.Add(new MultipartFormDataContent()); foreach (var o in postParameters.ToCollection()) { if (Array.IndexOf(fileParameterNames, o.Key) >= 0) { string filename = o.Value; FileStream fs = objectsToDispose.Add(new FileStream(filename, FileMode.Open)); HttpContent v = objectsToDispose.Add(new StreamContent(fs)); content.Add(v, o.Key, Path.GetFileName(filename)); } else { HttpContent v = objectsToDispose.Add(new StringContent(o.Value)); content.Add(v, o.Key); } } return(await SendMessageAsync(HttpMethod.Post, uri, content)); } }
/// <summary> /// Send a message and get the result. /// Deal with rate limiting return values and redirects. /// </summary> /// <param name="method">Get/Post/etc.</param> /// <param name="uri">The full Uri you want to call (including any get parameters)</param> /// <param name="postParameters">Post parameters as an object or JObject</param> public async Task <HttpResponseMessage> SendMessageAsyncAndGetResponse(HttpMethod method, string uri, object postParameters = null) { LastRequest = ""; LastResponse = ""; for (; ;) { if (Settings.DelayBetweenApiCalls > 0) { double elapsedSinceLastRequest = (DateTime.Now - lastRequest).TotalMilliseconds; if (elapsedSinceLastRequest < Settings.DelayBetweenApiCalls) { await Task.Delay((int)(Settings.DelayBetweenApiCalls - elapsedSinceLastRequest)); } } lastRequest = DateTime.Now; string content = null; using (DisposableCollection disposeMe = new DisposableCollection()) { var message = disposeMe.Add(new HttpRequestMessage(method, uri)); if (!string.IsNullOrEmpty(Settings.ApiKey)) { message.Headers.Add("Api-Key", Settings.ApiKey); } if (!string.IsNullOrEmpty(Settings.ApiUsername)) { message.Headers.Add("Api-Username", Settings.ApiUsername); } message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); message.Headers.Add("User-Agent", Settings.ApplicationName); if (postParameters != null) { if (postParameters is FileStream f) { content = Path.GetFileName(f.Name); f.Position = 0; message.Content = disposeMe.Add(new StreamContent(f)); string contentType = MimeMapping.MimeUtility.GetMimeMapping(content); message.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType); message.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = content }; message.Content.Headers.ContentLength = f.Length; content = "File: " + content; } else if (postParameters is HttpContent parameters) { message.Content = parameters; } else { var postParamsList = postParameters.ToCollection().ToList(); var form = new MultipartFormDataContent(); if (Settings.SkipValidations) { postParamsList.Add(new KeyValuePair <string, string>("skip_validations", "true")); } foreach (var kvp in postParamsList) { form.Add(new StringContent(kvp.Value), kvp.Key); } content = postParamsList.ToJson(); message.Content = disposeMe.Add(form); } } else if (method != HttpMethod.Get && Settings.SkipValidations) { // add in a skip_validations parameter if required for methods that change objects var postParamsList = new List <KeyValuePair <string, string> > { new KeyValuePair <string, string>("skip_validations", "true") }; content = postParamsList.ToJson(); message.Content = disposeMe.Add(new FormUrlEncodedContent(postParamsList)); } LastRequest = $"{message}:{content}"; HttpResponseMessage result; int backoff = 1000; int delay; if (Settings.LogRequest > 0) { Log($"Sent -> {(Settings.LogRequest > 1 ? message.ToString() : message.RequestUri.ToString())}:{content}"); } result = await _client.SendAsync(message); LastResponse = result.ToString(); if (Settings.LogResult > 1) { Log($"Received -> {result}"); } switch (result.StatusCode) { case HttpStatusCode.Found: // Redirect uri = result.Headers.Location.AbsoluteUri; delay = 1; break; case (HttpStatusCode)429: // TooManyRequests IEnumerable <string> values; delay = 5000; if (result.Headers.TryGetValues("Retry-After", out values)) { try { int d = 1000 * int.Parse(values.FirstOrDefault()); if (d > delay) { delay = d; } } catch { } } break; case HttpStatusCode.BadGateway: case HttpStatusCode.ServiceUnavailable: case HttpStatusCode.GatewayTimeout: backoff *= 2; delay = backoff; if (delay > 16000) { return(result); } break; default: return(result); } result.Dispose(); await Task.Delay(delay); } } }