From 25a21fd34210231d2e4aa77746c2d64179ff2cad Mon Sep 17 00:00:00 2001 From: Jonas Dellinger Date: Wed, 31 May 2017 17:25:56 +0200 Subject: [PATCH] Switched from WebClient to HTTPClient --- SpotifyAPI.Example/WebControl.cs | 2 +- SpotifyAPI.Tests/TestClass.cs | 15 +- SpotifyAPI/SpotifyAPI.csproj | 1 + SpotifyAPI/Web/IClient.cs | 44 ++-- SpotifyAPI/Web/SpotifyWebAPI.cs | 32 +-- SpotifyAPI/Web/SpotifyWebClient.cs | 324 +++++++++++++---------------- 6 files changed, 191 insertions(+), 227 deletions(-) diff --git a/SpotifyAPI.Example/WebControl.cs b/SpotifyAPI.Example/WebControl.cs index 02ab10bd..378a9c66 100644 --- a/SpotifyAPI.Example/WebControl.cs +++ b/SpotifyAPI.Example/WebControl.cs @@ -38,7 +38,7 @@ namespace SpotifyAPI.Example } authButton.Enabled = false; - _profile = _spotify.GetPrivateProfile(); + _profile = await _spotify.GetPrivateProfileAsync(); _savedTracks = GetSavedTracks(); savedTracksCountLabel.Text = _savedTracks.Count.ToString(); diff --git a/SpotifyAPI.Tests/TestClass.cs b/SpotifyAPI.Tests/TestClass.cs index 91a124f2..eaf99f49 100644 --- a/SpotifyAPI.Tests/TestClass.cs +++ b/SpotifyAPI.Tests/TestClass.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using SpotifyAPI.Web; using SpotifyAPI.Web.Models; using System; +using System.Collections.Generic; using System.IO; using System.Linq; @@ -50,23 +51,29 @@ namespace SpotifyAPI.Tests public void ShouldGetPrivateProfile_WithAuth() { PrivateProfile profile = GetFixture("private-user.json"); - _mock.Setup(client => client.DownloadJson(It.IsAny())) + _mock.Setup(client => client.DownloadJson(It.IsAny(), It.IsAny>())) .Returns(new Tuple(ResponseInfo.Empty, profile)); _spotify.UseAuth = true; Assert.AreEqual(profile, _spotify.GetPrivateProfile()); - _mock.Verify(client => client.DownloadJson(It.Is(str => ContainsValues(str, "/me"))), Times.Exactly(1)); + _mock.Verify(client => client.DownloadJson( + It.Is(str => ContainsValues(str, "/me")), + It.IsNotNull>()), Times.Exactly(1)); } [Test] public void ShouldGetPublicProfile() { PublicProfile profile = GetFixture("public-user.json"); - _mock.Setup(client => client.DownloadJson(It.IsAny())) + _mock.Setup(client => client.DownloadJson(It.IsAny(), It.IsAny>())) .Returns(new Tuple(ResponseInfo.Empty, profile)); + + _spotify.UseAuth = false; Assert.AreEqual(profile, _spotify.GetPublicProfile("wizzler")); - _mock.Verify(client => client.DownloadJson(It.Is(str => ContainsValues(str, "/users/wizzler"))), Times.Exactly(1)); + _mock.Verify(client => client.DownloadJson( + It.Is(str => ContainsValues(str, "/users/wizzler")), + It.Is>(headers => headers.Count == 0)), Times.Exactly(1)); } //Will add more tests once I decided if this is worth the effort (propably not?) diff --git a/SpotifyAPI/SpotifyAPI.csproj b/SpotifyAPI/SpotifyAPI.csproj index 46e500b4..df4bd6e2 100644 --- a/SpotifyAPI/SpotifyAPI.csproj +++ b/SpotifyAPI/SpotifyAPI.csproj @@ -45,6 +45,7 @@ + diff --git a/SpotifyAPI/Web/IClient.cs b/SpotifyAPI/Web/IClient.cs index 575b7671..86e4ed2e 100644 --- a/SpotifyAPI/Web/IClient.cs +++ b/SpotifyAPI/Web/IClient.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; +using System.Net.Http.Headers; using System.Threading.Tasks; using SpotifyAPI.Web.Models; @@ -15,28 +16,28 @@ namespace SpotifyAPI.Web /// /// An URL /// - Tuple Download(string url); + Tuple Download(string url, Dictionary headers = null); /// /// Downloads data async from an URL and returns it /// /// /// - Task> DownloadAsync(string url); + Task> DownloadAsync(string url, Dictionary headers = null); /// /// Downloads data from an URL and returns it /// /// An URL /// - Tuple DownloadRaw(string url); + Tuple DownloadRaw(string url, Dictionary headers = null); /// /// Downloads data async from an URL and returns it /// /// /// - Task> DownloadRawAsync(string url); + Task> DownloadRawAsync(string url, Dictionary headers = null); /// /// Downloads data from an URL and converts it to an object @@ -44,7 +45,7 @@ namespace SpotifyAPI.Web /// The Type which the object gets converted to /// An URL /// - Tuple DownloadJson(string url); + Tuple DownloadJson(string url, Dictionary headers = null); /// /// Downloads data async from an URL and converts it to an object @@ -52,7 +53,7 @@ namespace SpotifyAPI.Web /// The Type which the object gets converted to /// An URL /// - Task> DownloadJsonAsync(string url); + Task> DownloadJsonAsync(string url, Dictionary headers = null); /// /// Uploads data from an URL and returns the response @@ -61,7 +62,7 @@ namespace SpotifyAPI.Web /// The Body-Data (most likely a JSON String) /// The Upload-method (POST,DELETE,PUT) /// - Tuple Upload(string url, string body, string method); + Tuple Upload(string url, string body, string method, Dictionary headers = null); /// /// Uploads data async from an URL and returns the response @@ -70,7 +71,7 @@ namespace SpotifyAPI.Web /// The Body-Data (most likely a JSON String) /// The Upload-method (POST,DELETE,PUT) /// - Task> UploadAsync(string url, string body, string method); + Task> UploadAsync(string url, string body, string method, Dictionary headers = null); /// /// Uploads data from an URL and returns the response @@ -79,7 +80,7 @@ namespace SpotifyAPI.Web /// The Body-Data (most likely a JSON String) /// The Upload-method (POST,DELETE,PUT) /// - Tuple UploadRaw(string url, string body, string method); + Tuple UploadRaw(string url, string body, string method, Dictionary headers = null); /// /// Uploads data async from an URL and returns the response @@ -88,7 +89,7 @@ namespace SpotifyAPI.Web /// The Body-Data (most likely a JSON String) /// The Upload-method (POST,DELETE,PUT) /// - Task> UploadRawAsync(string url, string body, string method); + Task> UploadRawAsync(string url, string body, string method, Dictionary headers = null); /// /// Uploads data from an URL and converts the response to an object @@ -98,7 +99,7 @@ namespace SpotifyAPI.Web /// The Body-Data (most likely a JSON String) /// The Upload-method (POST,DELETE,PUT) /// - Tuple UploadJson(string url, string body, string method); + Tuple UploadJson(string url, string body, string method, Dictionary headers = null); /// /// Uploads data async from an URL and converts the response to an object @@ -108,25 +109,6 @@ namespace SpotifyAPI.Web /// The Body-Data (most likely a JSON String) /// The Upload-method (POST,DELETE,PUT) /// - Task> UploadJsonAsync(string url, string body, string method); - - /// - /// Sets a specific Header - /// - /// Header name - /// Header value - void SetHeader(string header, string value); - - /// - /// Removes a specific Header - /// - /// Header name - void RemoveHeader(string header); - - /// - /// Gets all current Headers - /// - /// A collection of Header KeyValue Pairs - List> GetHeaders(); + Task> UploadJsonAsync(string url, string body, string method, Dictionary headers = null); } } \ No newline at end of file diff --git a/SpotifyAPI/Web/SpotifyWebAPI.cs b/SpotifyAPI/Web/SpotifyWebAPI.cs index 1d721966..c41e562b 100644 --- a/SpotifyAPI/Web/SpotifyWebAPI.cs +++ b/SpotifyAPI/Web/SpotifyWebAPI.cs @@ -2115,11 +2115,14 @@ namespace SpotifyAPI.Web Tuple response = null; do { - WebClient.SetHeader("Authorization", TokenType + " " + AccessToken); - WebClient.SetHeader("Content-Type", "application/json"); + Dictionary headers = new Dictionary + { + { "Authorization", TokenType + " " + AccessToken}, + { "Content-Type", "application/json" } + }; if (response != null) { Thread.Sleep(RetryAfter); } - response = WebClient.UploadJson(url, uploadData, method); + response = WebClient.UploadJson(url, uploadData, method, headers); response.Item2.AddResponseInfo(response.Item1); lastError = response.Item2.Error; @@ -2142,11 +2145,14 @@ namespace SpotifyAPI.Web Tuple response = null; do { - WebClient.SetHeader("Authorization", TokenType + " " + AccessToken); - WebClient.SetHeader("Content-Type", "application/json"); + Dictionary headers = new Dictionary + { + { "Authorization", TokenType + " " + AccessToken}, + { "Content-Type", "application/json" } + }; if (response != null) { await Task.Delay(RetryAfter).ConfigureAwait(false); } - response = await WebClient.UploadJsonAsync(url, uploadData, method).ConfigureAwait(false); + response = await WebClient.UploadJsonAsync(url, uploadData, method, headers).ConfigureAwait(false); response.Item2.AddResponseInfo(response.Item1); lastError = response.Item2.Error; @@ -2204,20 +2210,18 @@ namespace SpotifyAPI.Web private Tuple DownloadDataAlt(string url) { + Dictionary headers = new Dictionary(); if (UseAuth) - WebClient.SetHeader("Authorization", TokenType + " " + AccessToken); - else - WebClient.RemoveHeader("Authorization"); - return WebClient.DownloadJson(url); + headers.Add("Authorization", TokenType + " " + AccessToken); + return WebClient.DownloadJson(url, headers); } private Task> DownloadDataAltAsync(string url) { + Dictionary headers = new Dictionary(); if (UseAuth) - WebClient.SetHeader("Authorization", TokenType + " " + AccessToken); - else - WebClient.RemoveHeader("Authorization"); - return WebClient.DownloadJsonAsync(url); + headers.Add("Authorization", TokenType + " " + AccessToken); + return WebClient.DownloadJsonAsync(url, headers); } #endregion Util diff --git a/SpotifyAPI/Web/SpotifyWebClient.cs b/SpotifyAPI/Web/SpotifyWebClient.cs index 327f6039..24077b5b 100644 --- a/SpotifyAPI/Web/SpotifyWebClient.cs +++ b/SpotifyAPI/Web/SpotifyWebClient.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using SpotifyAPI.Web.Models; @@ -14,198 +16,166 @@ namespace SpotifyAPI.Web { public JsonSerializerSettings JsonSettings { get; set; } - private readonly WebClient _webClient; private readonly Encoding _encoding = Encoding.UTF8; - public SpotifyWebClient() + public Tuple Download(string url, Dictionary headers = null) { - _webClient = new WebClient() + Tuple raw = DownloadRaw(url, headers); + return new Tuple(raw.Item1, raw.Item2.Length > 0 ? _encoding.GetString(raw.Item2) : "{}"); + } + + public async Task> DownloadAsync(string url, Dictionary headers = null) + { + Tuple raw = await DownloadRawAsync(url, headers).ConfigureAwait(false); + return new Tuple(raw.Item1, raw.Item2.Length > 0 ? _encoding.GetString(raw.Item2) : "{}"); + } + + public Tuple DownloadRaw(string url, Dictionary headers = null) + { + using (HttpClient client = new HttpClient()) { - Proxy = null, - Encoding = _encoding - }; + if (headers != null) + { + foreach (KeyValuePair headerPair in headers) + { + client.DefaultRequestHeaders.Add(headerPair.Key, headerPair.Value); + } + } + using (HttpResponseMessage response = Task.Run(() => client.GetAsync(url)).Result) + { + return new Tuple(new ResponseInfo() + { + Headers = ConvertHeaders(response.Headers) + }, Task.Run(() => response.Content.ReadAsByteArrayAsync()).Result); + } + } + } + + public async Task> DownloadRawAsync(string url, Dictionary headers = null) + { + using (HttpClient client = new HttpClient()) + { + if (headers != null) + { + foreach (KeyValuePair headerPair in headers) + { + client.DefaultRequestHeaders.Add(headerPair.Key, headerPair.Value); + } + } + using (HttpResponseMessage response = await client.GetAsync(url).ConfigureAwait(false)) + { + return new Tuple(new ResponseInfo() + { + Headers = ConvertHeaders(response.Headers) + }, await response.Content.ReadAsByteArrayAsync()); + } + } + } + + public Tuple DownloadJson(string url, Dictionary headers = null) + { + Tuple response = Download(url, headers); + return new Tuple(response.Item1, JsonConvert.DeserializeObject(response.Item2, JsonSettings)); + } + + public async Task> DownloadJsonAsync(string url, Dictionary headers = null) + { + Tuple response = await DownloadAsync(url, headers).ConfigureAwait(false); + return new Tuple(response.Item1, JsonConvert.DeserializeObject(response.Item2, JsonSettings)); + } + + public Tuple Upload(string url, string body, string method, Dictionary headers = null) + { + Tuple data = UploadRaw(url, body, method, headers); + return new Tuple(data.Item1, data.Item2.Length > 0 ? _encoding.GetString(data.Item2) : "{}"); + } + + public async Task> UploadAsync(string url, string body, string method, Dictionary headers = null) + { + Tuple data = await UploadRawAsync(url, body, method, headers).ConfigureAwait(false); + return new Tuple(data.Item1, data.Item2.Length > 0 ? _encoding.GetString(data.Item2) : "{}"); + } + + public Tuple UploadRaw(string url, string body, string method, Dictionary headers = null) + { + using (HttpClient client = new HttpClient()) + { + if (headers != null) + { + foreach (KeyValuePair headerPair in headers) + { + client.DefaultRequestHeaders.Add(headerPair.Key, headerPair.Value); + } + } + + HttpRequestMessage message = new HttpRequestMessage(new HttpMethod(method), url) + { + Content = new StringContent(body, _encoding) + }; + using (HttpResponseMessage response = Task.Run(() => client.SendAsync(message)).Result) + { + return new Tuple(new ResponseInfo + { + Headers = ConvertHeaders(response.Headers) + }, Task.Run(() => response.Content.ReadAsByteArrayAsync()).Result); + } + } + } + + public async Task> UploadRawAsync(string url, string body, string method, Dictionary headers = null) + { + using (HttpClient client = new HttpClient()) + { + if (headers != null) + { + foreach (KeyValuePair headerPair in headers) + { + client.DefaultRequestHeaders.Add(headerPair.Key, headerPair.Value); + } + } + + HttpRequestMessage message = new HttpRequestMessage(new HttpMethod(method), url) + { + Content = new StringContent(body, _encoding) + }; + using (HttpResponseMessage response = await client.SendAsync(message)) + { + return new Tuple(new ResponseInfo + { + Headers = ConvertHeaders(response.Headers) + }, await response.Content.ReadAsByteArrayAsync()); + } + } + } + + public Tuple UploadJson(string url, string body, string method, Dictionary headers = null) + { + Tuple response = Upload(url, body, method, headers); + return new Tuple(response.Item1, JsonConvert.DeserializeObject(response.Item2, JsonSettings)); + } + + public async Task> UploadJsonAsync(string url, string body, string method, Dictionary headers = null) + { + Tuple response = await UploadAsync(url, body, method, headers).ConfigureAwait(false); + return new Tuple(response.Item1, JsonConvert.DeserializeObject(response.Item2, JsonSettings)); } public void Dispose() { - _webClient.Dispose(); + GC.SuppressFinalize(this); } - public Tuple Download(string url) + private static WebHeaderCollection ConvertHeaders(HttpResponseHeaders headers) { - Tuple response; - try + WebHeaderCollection newHeaders = new WebHeaderCollection(); + foreach (KeyValuePair> headerPair in headers) { - Tuple raw = DownloadRaw(url); - response = new Tuple(raw.Item1, raw.Item2.Length > 0 ? _encoding.GetString(raw.Item2) : "{}"); - } - catch (WebException e) - { - using (StreamReader reader = new StreamReader(e.Response.GetResponseStream())) + foreach (string headerValue in headerPair.Value) { - response = new Tuple(new ResponseInfo - { - Headers = _webClient.ResponseHeaders - }, reader.ReadToEnd()); + newHeaders.Add(headerPair.Key, headerValue); } } - return response; - } - - public async Task> DownloadAsync(string url) - { - Tuple response; - try - { - Tuple raw = await DownloadRawAsync(url).ConfigureAwait(false); - response = new Tuple(raw.Item1, raw.Item2.Length > 0 ? _encoding.GetString(raw.Item2) : "{}"); - } - catch (WebException e) - { - using (StreamReader reader = new StreamReader(e.Response.GetResponseStream())) - { - response = new Tuple(new ResponseInfo - { - Headers = _webClient.ResponseHeaders - }, reader.ReadToEnd()); - } - } - return response; - } - - public Tuple DownloadRaw(string url) - { - byte[] data = _webClient.DownloadData(url); - ResponseInfo info = new ResponseInfo() - { - Headers = _webClient.ResponseHeaders - }; - return new Tuple(info, data); - } - - public async Task> DownloadRawAsync(string url) - { - using (WebClient webClient = new WebClient()) - { - webClient.Proxy = null; - webClient.Encoding = _encoding; - webClient.Headers = _webClient.Headers; - - byte[] data = await _webClient.DownloadDataTaskAsync(url).ConfigureAwait(false); - ResponseInfo info = new ResponseInfo() - { - Headers = webClient.ResponseHeaders - }; - return new Tuple(info, data); - } - } - - public Tuple DownloadJson(string url) - { - Tuple response = Download(url); - return new Tuple(response.Item1, JsonConvert.DeserializeObject(response.Item2, JsonSettings)); - } - - public async Task> DownloadJsonAsync(string url) - { - Tuple response = await DownloadAsync(url).ConfigureAwait(false); - return new Tuple(response.Item1, JsonConvert.DeserializeObject(response.Item2, JsonSettings)); - } - - public Tuple Upload(string url, string body, string method) - { - Tuple response; - try - { - Tuple data = UploadRaw(url, body, method); - response = new Tuple(data.Item1, data.Item2.Length > 0 ? _encoding.GetString(data.Item2) : "{}"); - } - catch (WebException e) - { - using (StreamReader reader = new StreamReader(e.Response.GetResponseStream())) - { - response = new Tuple(new ResponseInfo - { - Headers = _webClient.ResponseHeaders - }, reader.ReadToEnd()); - } - } - return response; - } - - public async Task> UploadAsync(string url, string body, string method) - { - Tuple response; - try - { - Tuple data = await UploadRawAsync(url, body, method).ConfigureAwait(false); - response = new Tuple(data.Item1, data.Item2.Length > 0 ? _encoding.GetString(data.Item2) : "{}"); - } - catch (WebException e) - { - using (StreamReader reader = new StreamReader(e.Response.GetResponseStream())) - { - response = new Tuple(new ResponseInfo - { - Headers = _webClient.ResponseHeaders - }, reader.ReadToEnd()); - } - } - return response; - } - - public Tuple UploadRaw(string url, string body, string method) - { - byte[] data = _webClient.UploadData(url, method, _encoding.GetBytes(body)); - ResponseInfo info = new ResponseInfo - { - Headers = _webClient.ResponseHeaders - }; - return new Tuple(info, data); - } - - public async Task> UploadRawAsync(string url, string body, string method) - { - using (WebClient webClient = new WebClient()) - { - webClient.Proxy = null; - webClient.Encoding = _encoding; - webClient.Headers = _webClient.Headers; - byte[] data = await _webClient.UploadDataTaskAsync(url, method, _encoding.GetBytes(body)).ConfigureAwait(false); - ResponseInfo info = new ResponseInfo - { - Headers = _webClient.ResponseHeaders - }; - return new Tuple(info, data); - } - } - - public Tuple UploadJson(string url, string body, string method) - { - Tuple response = Upload(url, body, method); - return new Tuple(response.Item1, JsonConvert.DeserializeObject(response.Item2, JsonSettings)); - } - - public async Task> UploadJsonAsync(string url, string body, string method) - { - Tuple response = await UploadAsync(url, body, method).ConfigureAwait(false); - return new Tuple(response.Item1, JsonConvert.DeserializeObject(response.Item2, JsonSettings)); - } - - public void SetHeader(string header, string value) - { - _webClient.Headers[header] = value; - } - - public void RemoveHeader(string header) - { - if (_webClient.Headers[header] != null) - _webClient.Headers.Remove(header); - } - - public List> GetHeaders() - { - return _webClient.Headers.AllKeys.Select(header => new KeyValuePair(header, _webClient.Headers[header])).ToList(); + return newHeaders; } } } \ No newline at end of file