protected async Task<JsonValue> Fetch( Uri endPoint, RequestType requestType, bool needsToken, CancellationToken cancel, IProgress<ProgressChangedEventArgs> progress = null, IDictionary<string, string> postData = null, KeyValuePair<string,string>? overrideAuth = null) { if (endPoint == null) throw new ArgumentNullException("endPoint"); var wc = new WebClient(); if (needsToken && !Credentials.HasValue) throw new InvalidOperationException("Cannot access this part of the Quizlet service without logging in."); wc.Headers["User-Agent"] = "FlashcardsWP8/0.1 (" + Environment.OSVersion + "; .NET " + Environment.Version + ")"; // Overriding is used for basic authentication for OAuth process. bool useClientID = false; if (overrideAuth != null) wc.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(overrideAuth.Value.Key + ":" + overrideAuth.Value.Value)); else if (Credentials != null) wc.Headers["Authorization"] = "Bearer " + Credentials.Value.ApiToken; else useClientID = true; try { string result; if (requestType == RequestType.Get) { var uri = new Uri(ServiceUri, endPoint); uri = new Uri(uri + (uri.Query == "" ? "?" : "&") + "client_id=" + clientID); result = await wc.DownloadStringAsTask(uri, cancel, progress); } else { var sb = new StringBuilder(); wc.Headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8"; wc.Encoding = Encoding.UTF8; if (postData != null) { if (useClientID) postData["client_id"] = clientID; foreach (var kvp in postData) { if (sb.Length > 0) sb.Append('&'); sb.Append(kvp.Key).Append('=').Append(Uri.EscapeDataString(kvp.Value)); } } result = await wc.UploadStringAsTask(new Uri(ServiceUri, endPoint), requestType.ToString().ToUpperInvariant(), sb.ToString(), cancel, progress); } JsonValue json = null; // In case of HTTP status 204 (no content). This is produced, for example, as the result of // a DELETE call to an endpoint. if (!string.IsNullOrWhiteSpace(result)) json = JsonValue.Parse(new StringReader(result)); return json; } catch (WebException e) { // The Quizlet API returns a JSON document explaining the error if there is one, // so try to use that if possible. try { if (e.Response != null) { var response = (e.Response as HttpWebResponse).GetResponseStream(); using (var reader = new StreamReader(response)) { var dict = JsonDictionary.FromValue(JsonValue.Parse(reader)); var ctx = new JsonContext(); var errorCode = ctx.FromJson<string>(dict["error"]); var errorText = ctx.FromJson<string>(dict["error_description"]); if (errorCode != null && errorText != null) { switch (errorCode) { case "invalid_access": throw new AccessDeniedException(errorText); case "item_not_found": throw new ItemNotFoundException(errorText); case "item_deleted": throw new ItemDeletedException(errorText); default: throw new QuizletException(errorText); } } } } throw new QuizletException(string.Format("Unable to contact the Quizlet server ({0}).", e.Message), e); } catch (FormatException) { // Not JSON or invalid - just wrap the original exception throw new QuizletException(e.Message, e); } catch (KeyNotFoundException) { throw new QuizletException(e.Message, e); } } }