diff --git a/SpotifyAPI.Example/WebControl.cs b/SpotifyAPI.Example/WebControl.cs index 5d7f88df..1c0f3a39 100644 --- a/SpotifyAPI.Example/WebControl.cs +++ b/SpotifyAPI.Example/WebControl.cs @@ -4,7 +4,6 @@ using SpotifyAPI.Web.Enums; using SpotifyAPI.Web.Models; using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Net; diff --git a/SpotifyAPI.Tests/TestClass.cs b/SpotifyAPI.Tests/TestClass.cs index 25815a29..04151142 100644 --- a/SpotifyAPI.Tests/TestClass.cs +++ b/SpotifyAPI.Tests/TestClass.cs @@ -50,7 +50,7 @@ namespace SpotifyAPI.Tests public void ShouldGetPrivateProfile_WithAuth() { PrivateProfile profile = GetFixture("private-user.json"); - _mock.Setup(client => client.DownloadJson(It.IsAny())).Returns(profile); + _mock.Setup(client => client.DownloadJson(It.IsAny())).Returns(new Tuple(null, profile)); _spotify.UseAuth = true; Assert.AreEqual(profile, _spotify.GetPrivateProfile()); @@ -61,7 +61,7 @@ namespace SpotifyAPI.Tests public void ShouldGetPublicProfile() { PublicProfile profile = GetFixture("public-user.json"); - _mock.Setup(client => client.DownloadJson(It.IsAny())).Returns(profile); + _mock.Setup(client => client.DownloadJson(It.IsAny())).Returns(new Tuple(null, profile)); Assert.AreEqual(profile, _spotify.GetPublicProfile("wizzler")); _mock.Verify(client => client.DownloadJson(It.Is(str => ContainsValues(str, "/users/wizzler"))), Times.Exactly(1)); diff --git a/SpotifyAPI/SpotifyAPI.csproj b/SpotifyAPI/SpotifyAPI.csproj index 65032259..3f025e4b 100644 --- a/SpotifyAPI/SpotifyAPI.csproj +++ b/SpotifyAPI/SpotifyAPI.csproj @@ -92,6 +92,7 @@ + diff --git a/SpotifyAPI/Web/IClient.cs b/SpotifyAPI/Web/IClient.cs index fd198287..575b7671 100644 --- a/SpotifyAPI/Web/IClient.cs +++ b/SpotifyAPI/Web/IClient.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using SpotifyAPI.Web.Models; namespace SpotifyAPI.Web { @@ -14,28 +15,28 @@ namespace SpotifyAPI.Web /// /// An URL /// - string Download(string url); + Tuple Download(string url); /// /// Downloads data async from an URL and returns it /// /// /// - Task DownloadAsync(string url); + Task> DownloadAsync(string url); /// /// Downloads data from an URL and returns it /// /// An URL /// - byte[] DownloadRaw(string url); + Tuple DownloadRaw(string url); /// /// Downloads data async from an URL and returns it /// /// /// - Task DownloadRawAsync(string url); + Task> DownloadRawAsync(string url); /// /// Downloads data from an URL and converts it to an object @@ -43,7 +44,7 @@ namespace SpotifyAPI.Web /// The Type which the object gets converted to /// An URL /// - T DownloadJson(string url); + Tuple DownloadJson(string url); /// /// Downloads data async from an URL and converts it to an object @@ -51,7 +52,7 @@ namespace SpotifyAPI.Web /// The Type which the object gets converted to /// An URL /// - Task DownloadJsonAsync(string url); + Task> DownloadJsonAsync(string url); /// /// Uploads data from an URL and returns the response @@ -60,7 +61,7 @@ namespace SpotifyAPI.Web /// The Body-Data (most likely a JSON String) /// The Upload-method (POST,DELETE,PUT) /// - string Upload(string url, string body, string method); + Tuple Upload(string url, string body, string method); /// /// Uploads data async from an URL and returns the response @@ -69,7 +70,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); /// /// Uploads data from an URL and returns the response @@ -78,7 +79,7 @@ namespace SpotifyAPI.Web /// The Body-Data (most likely a JSON String) /// The Upload-method (POST,DELETE,PUT) /// - byte[] UploadRaw(string url, string body, string method); + Tuple UploadRaw(string url, string body, string method); /// /// Uploads data async from an URL and returns the response @@ -87,7 +88,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); /// /// Uploads data from an URL and converts the response to an object @@ -97,7 +98,7 @@ namespace SpotifyAPI.Web /// The Body-Data (most likely a JSON String) /// The Upload-method (POST,DELETE,PUT) /// - T UploadJson(string url, string body, string method); + Tuple UploadJson(string url, string body, string method); /// /// Uploads data async from an URL and converts the response to an object @@ -107,7 +108,7 @@ 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); + Task> UploadJsonAsync(string url, string body, string method); /// /// Sets a specific Header diff --git a/SpotifyAPI/Web/Models/BasicModel.cs b/SpotifyAPI/Web/Models/BasicModel.cs index bb968613..052d222a 100644 --- a/SpotifyAPI/Web/Models/BasicModel.cs +++ b/SpotifyAPI/Web/Models/BasicModel.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json; using System; +using System.Net; namespace SpotifyAPI.Web.Models { @@ -8,9 +9,14 @@ namespace SpotifyAPI.Web.Models [JsonProperty("error")] public Error Error { get; set; } - public Boolean HasError() - { - return Error != null; - } + private WebHeaderCollection _headers; + + public bool HasError() => Error != null; + + public void AddResponseInfo(ResponseInfo info) => _headers = info.Headers; + + public string Header(string key) => _headers?.Get(key); + + public WebHeaderCollection Headers() => _headers; } } \ No newline at end of file diff --git a/SpotifyAPI/Web/Models/ResponseInfo.cs b/SpotifyAPI/Web/Models/ResponseInfo.cs new file mode 100644 index 00000000..b1fc96ea --- /dev/null +++ b/SpotifyAPI/Web/Models/ResponseInfo.cs @@ -0,0 +1,9 @@ +using System.Net; + +namespace SpotifyAPI.Web.Models +{ + public class ResponseInfo + { + public WebHeaderCollection Headers { get; set; } + } +} \ No newline at end of file diff --git a/SpotifyAPI/Web/SpotifyWebAPI.cs b/SpotifyAPI/Web/SpotifyWebAPI.cs index ad1b56b5..d8795ab2 100644 --- a/SpotifyAPI/Web/SpotifyWebAPI.cs +++ b/SpotifyAPI/Web/SpotifyWebAPI.cs @@ -35,7 +35,7 @@ namespace SpotifyAPI.Web public string TokenType { get; set; } public string AccessToken { get; set; } - public Boolean UseAuth { get; set; } + public bool UseAuth { get; set; } public IClient WebClient { get; set; } public void Dispose() @@ -693,14 +693,26 @@ namespace SpotifyAPI.Web /// A list of the artist or the user Spotify IDs to check /// /// AUTH NEEDED - public ListResponse IsFollowing(FollowType followType, List ids) + public ListResponse IsFollowing(FollowType followType, List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for IsFollowing"); - JToken res = DownloadData(_builder.IsFollowing(followType, ids)); - if (res is JArray) - return new ListResponse { List = res.ToObject>(), Error = null }; - return new ListResponse { List = null, Error = res["error"].ToObject() }; + Tuple res = DownloadDataAlt(_builder.IsFollowing(followType, ids)); + ListResponse ret; + if (res.Item2 is JArray) + ret = new ListResponse + { + List = res.Item2.ToObject>(), + Error = null + }; + else + ret = new ListResponse + { + List = null, + Error = res.Item2["error"].ToObject() + }; + ret.AddResponseInfo(res.Item1); + return ret; } /// @@ -710,14 +722,26 @@ namespace SpotifyAPI.Web /// A list of the artist or the user Spotify IDs to check /// /// AUTH NEEDED - public async Task> IsFollowingAsync(FollowType followType, List ids) + public async Task> IsFollowingAsync(FollowType followType, List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for IsFollowing"); - JToken res = await DownloadDataAsync(_builder.IsFollowing(followType, ids)); - if (res is JArray) - return new ListResponse { List = res.ToObject>(), Error = null }; - return new ListResponse { List = null, Error = res["error"].ToObject() }; + Tuple res = await DownloadDataAltAsync(_builder.IsFollowing(followType, ids)); + ListResponse ret = null; + if (res.Item2 is JArray) + ret = new ListResponse + { + List = res.Item2.ToObject>(), + Error = null + }; + else + ret = new ListResponse + { + List = null, + Error = res.Item2["error"].ToObject() + }; + ret.AddResponseInfo(res.Item1); + return ret; } /// @@ -727,7 +751,7 @@ namespace SpotifyAPI.Web /// Artists or the Users Spotify ID /// /// AUTH NEEDED - public ListResponse IsFollowing(FollowType followType, string id) + public ListResponse IsFollowing(FollowType followType, string id) { return IsFollowing(followType, new List { id }); } @@ -739,7 +763,7 @@ namespace SpotifyAPI.Web /// Artists or the Users Spotify ID /// /// AUTH NEEDED - public async Task> IsFollowingAsync(FollowType followType, string id) + public async Task> IsFollowingAsync(FollowType followType, string id) { return await IsFollowingAsync(followType, new List { id }); } @@ -822,14 +846,26 @@ namespace SpotifyAPI.Web /// A list of Spotify User IDs /// /// AUTH NEEDED - public ListResponse IsFollowingPlaylist(string ownerId, string playlistId, List ids) + public ListResponse IsFollowingPlaylist(string ownerId, string playlistId, List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for IsFollowingPlaylist"); - JToken res = DownloadData(_builder.IsFollowingPlaylist(ownerId, playlistId, ids)); - if (res is JArray) - return new ListResponse { List = res.ToObject>(), Error = null }; - return new ListResponse { List = null, Error = res["error"].ToObject() }; + Tuple res = DownloadDataAlt(_builder.IsFollowingPlaylist(ownerId, playlistId, ids)); + ListResponse ret = null; + if (res.Item2 is JArray) + ret = new ListResponse + { + List = res.Item2.ToObject>(), + Error = null + }; + else + ret = new ListResponse + { + List = null, + Error = res.Item2["error"].ToObject() + }; + ret.AddResponseInfo(res.Item1); + return ret; } /// @@ -840,14 +876,26 @@ namespace SpotifyAPI.Web /// A list of Spotify User IDs /// /// AUTH NEEDED - public async Task> IsFollowingPlaylistAsync(string ownerId, string playlistId, List ids) + public async Task> IsFollowingPlaylistAsync(string ownerId, string playlistId, List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for IsFollowingPlaylist"); - JToken res = await DownloadDataAsync(_builder.IsFollowingPlaylist(ownerId, playlistId, ids)); - if (res is JArray) - return new ListResponse { List = res.ToObject>(), Error = null }; - return new ListResponse { List = null, Error = res["error"].ToObject() }; + Tuple res = await DownloadDataAltAsync(_builder.IsFollowingPlaylist(ownerId, playlistId, ids)); + ListResponse ret = null; + if (res.Item2 is JArray) + ret = new ListResponse + { + List = res.Item2.ToObject>(), + Error = null + }; + else + ret = new ListResponse + { + List = null, + Error = res.Item2["error"].ToObject() + }; + ret.AddResponseInfo(res.Item1); + return ret; } /// @@ -858,7 +906,7 @@ namespace SpotifyAPI.Web /// A Spotify User ID /// /// AUTH NEEDED - public ListResponse IsFollowingPlaylist(string ownerId, string playlistId, string id) + public ListResponse IsFollowingPlaylist(string ownerId, string playlistId, string id) { return IsFollowingPlaylist(ownerId, playlistId, new List { id }); } @@ -871,7 +919,7 @@ namespace SpotifyAPI.Web /// A Spotify User ID /// /// AUTH NEEDED - public async Task> IsFollowingPlaylistAsync(string ownerId, string playlistId, string id) + public async Task> IsFollowingPlaylistAsync(string ownerId, string playlistId, string id) { return await IsFollowingPlaylistAsync(ownerId, playlistId, new List { id }); } @@ -986,14 +1034,26 @@ namespace SpotifyAPI.Web /// A list of the Spotify IDs. /// /// AUTH NEEDED - public ListResponse CheckSavedTracks(List ids) + public ListResponse CheckSavedTracks(List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for CheckSavedTracks"); - JToken res = DownloadData(_builder.CheckSavedTracks(ids)); - if (res is JArray) - return new ListResponse { List = res.ToObject>(), Error = null }; - return new ListResponse { List = null, Error = res["error"].ToObject() }; + Tuple res = DownloadDataAlt(_builder.CheckSavedTracks(ids)); + ListResponse ret = null; + if (res.Item2 is JArray) + ret = new ListResponse + { + List = res.Item2.ToObject>(), + Error = null + }; + else + ret = new ListResponse + { + List = null, + Error = res.Item2["error"].ToObject() + }; + ret.AddResponseInfo(res.Item1); + return ret; } /// @@ -1002,14 +1062,26 @@ namespace SpotifyAPI.Web /// A list of the Spotify IDs. /// /// AUTH NEEDED - public async Task> CheckSavedTracksAsync(List ids) + public async Task> CheckSavedTracksAsync(List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for CheckSavedTracks"); - JToken res = await DownloadDataAsync(_builder.CheckSavedTracks(ids)); - if (res is JArray) - return new ListResponse { List = res.ToObject>(), Error = null }; - return new ListResponse { List = null, Error = res["error"].ToObject() }; + Tuple res = await DownloadDataAltAsync(_builder.CheckSavedTracks(ids)); + ListResponse ret = null; + if (res.Item2 is JArray) + ret = new ListResponse + { + List = res.Item2.ToObject>(), + Error = null + }; + else + ret = new ListResponse + { + List = null, + Error = res.Item2["error"].ToObject() + }; + ret.AddResponseInfo(res.Item1); + return ret; } /// @@ -1118,14 +1190,26 @@ namespace SpotifyAPI.Web /// A list of the Spotify IDs. /// /// AUTH NEEDED - public ListResponse CheckSavedAlbums(List ids) + public ListResponse CheckSavedAlbums(List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for CheckSavedTracks"); - JToken res = DownloadData(_builder.CheckSavedAlbums(ids)); - if (res is JArray) - return new ListResponse { List = res.ToObject>(), Error = null }; - return new ListResponse { List = null, Error = res["error"].ToObject() }; + Tuple res = DownloadDataAlt(_builder.CheckSavedAlbums(ids)); + ListResponse ret = null; + if (res.Item2 is JArray) + ret = new ListResponse + { + List = res.Item2.ToObject>(), + Error = null + }; + else + ret = new ListResponse + { + List = null, + Error = res.Item2["error"].ToObject() + }; + ret.AddResponseInfo(res.Item1); + return ret; } /// @@ -1134,14 +1218,26 @@ namespace SpotifyAPI.Web /// A list of the Spotify IDs. /// /// AUTH NEEDED - public async Task> CheckSavedAlbumsAsync(List ids) + public async Task> CheckSavedAlbumsAsync(List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for CheckSavedAlbumsAsync"); - JToken res = await DownloadDataAsync(_builder.CheckSavedAlbums(ids)); - if (res is JArray) - return new ListResponse { List = res.ToObject>(), Error = null }; - return new ListResponse { List = null, Error = res["error"].ToObject() }; + Tuple res = await DownloadDataAltAsync(_builder.CheckSavedAlbums(ids)); + ListResponse ret = null; + if (res.Item2 is JArray) + ret = new ListResponse + { + List = res.Item2.ToObject>(), + Error = null + }; + else + ret = new ListResponse + { + List = null, + Error = res.Item2["error"].ToObject() + }; + ret.AddResponseInfo(res.Item1); + return ret; } #endregion Library @@ -1336,7 +1432,7 @@ namespace SpotifyAPI.Web /// /// /// AUTH NEEDED - public FullPlaylist CreatePlaylist(string userId, string playlistName, Boolean isPublic = true) + public FullPlaylist CreatePlaylist(string userId, string playlistName, bool isPublic = true) { JObject body = new JObject { @@ -1360,7 +1456,7 @@ namespace SpotifyAPI.Web /// /// /// AUTH NEEDED - public async Task CreatePlaylistAsync(string userId, string playlistName, Boolean isPublic = true) + public async Task CreatePlaylistAsync(string userId, string playlistName, bool isPublic = true) { JObject body = new JObject { @@ -1379,7 +1475,7 @@ namespace SpotifyAPI.Web /// If true the playlist will be public, if false it will be private. /// /// AUTH NEEDED - public ErrorResponse UpdatePlaylist(string userId, string playlistId, string newName = null, Boolean? newPublic = null) + public ErrorResponse UpdatePlaylist(string userId, string playlistId, string newName = null, bool? newPublic = null) { JObject body = new JObject(); if (newName != null) @@ -1398,7 +1494,7 @@ namespace SpotifyAPI.Web /// If true the playlist will be public, if false it will be private. /// /// AUTH NEEDED - public async Task UpdatePlaylistAsync(string userId, string playlistId, string newName = null, Boolean? newPublic = null) + public async Task UpdatePlaylistAsync(string userId, string playlistId, string newName = null, bool? newPublic = null) { JObject body = new JObject(); if (newName != null) @@ -1767,7 +1863,7 @@ namespace SpotifyAPI.Web #region Util - public TOut GetNextPage(Paging paging) + public TOut GetNextPage(Paging paging) where TOut : BasicModel { if (!paging.HasNextPage()) throw new InvalidOperationException("This Paging-Object has no Next-Page"); @@ -1779,7 +1875,7 @@ namespace SpotifyAPI.Web return GetNextPage, T>(paging); } - public async Task GetNextPageAsync(Paging paging) + public async Task GetNextPageAsync(Paging paging) where TOut : BasicModel { if (!paging.HasNextPage()) throw new InvalidOperationException("This Paging-Object has no Next-Page"); @@ -1791,7 +1887,7 @@ namespace SpotifyAPI.Web return await GetNextPageAsync, T>(paging); } - public TOut GetPreviousPage(Paging paging) + public TOut GetPreviousPage(Paging paging) where TOut : BasicModel { if (!paging.HasPreviousPage()) throw new InvalidOperationException("This Paging-Object has no Previous-Page"); @@ -1803,7 +1899,7 @@ namespace SpotifyAPI.Web return GetPreviousPage, T>(paging); } - public async Task GetPreviousPageAsync(Paging paging) + public async Task GetPreviousPageAsync(Paging paging) where TOut : BasicModel { if (!paging.HasPreviousPage()) throw new InvalidOperationException("This Paging-Object has no Previous-Page"); @@ -1815,25 +1911,55 @@ namespace SpotifyAPI.Web return await GetPreviousPageAsync, T>(paging); } - public T UploadData(string url, string uploadData, string method = "POST") + public T UploadData(string url, string uploadData, string method = "POST") where T : BasicModel { if (!UseAuth) throw new InvalidOperationException("Auth is required for all Upload-Actions"); WebClient.SetHeader("Authorization", TokenType + " " + AccessToken); WebClient.SetHeader("Content-Type", "application/json"); - return WebClient.UploadJson(url, uploadData, method); + + Tuple response = WebClient.UploadJson(url, uploadData, method); + response.Item2.AddResponseInfo(response.Item1); + return response.Item2; } - public Task UploadDataAsync(string url, string uploadData, string method = "POST") + public async Task UploadDataAsync(string url, string uploadData, string method = "POST") where T : BasicModel { if (!UseAuth) throw new InvalidOperationException("Auth is required for all Upload-Actions"); WebClient.SetHeader("Authorization", TokenType + " " + AccessToken); WebClient.SetHeader("Content-Type", "application/json"); - return WebClient.UploadJsonAsync(url, uploadData, method); + + Tuple response = await WebClient.UploadJsonAsync(url, uploadData, method); + response.Item2.AddResponseInfo(response.Item1); + return response.Item2; } - public T DownloadData(string url) + public T DownloadData(string url) where T : BasicModel + { + if (UseAuth) + WebClient.SetHeader("Authorization", TokenType + " " + AccessToken); + else + WebClient.RemoveHeader("Authorization"); + + Tuple response = WebClient.DownloadJson(url); + response.Item2.AddResponseInfo(response.Item1); + return response.Item2; + } + + public async Task DownloadDataAsync(string url) where T : BasicModel + { + if (UseAuth) + WebClient.SetHeader("Authorization", TokenType + " " + AccessToken); + else + WebClient.RemoveHeader("Authorization"); + + Tuple response = await WebClient.DownloadJsonAsync(url); + response.Item2.AddResponseInfo(response.Item1); + return response.Item2; + } + + private Tuple DownloadDataAlt(string url) { if (UseAuth) WebClient.SetHeader("Authorization", TokenType + " " + AccessToken); @@ -1842,7 +1968,7 @@ namespace SpotifyAPI.Web return WebClient.DownloadJson(url); } - public Task DownloadDataAsync(string url) + private Task> DownloadDataAltAsync(string url) { if (UseAuth) WebClient.SetHeader("Authorization", TokenType + " " + AccessToken); diff --git a/SpotifyAPI/Web/SpotifyWebClient.cs b/SpotifyAPI/Web/SpotifyWebClient.cs index 3249376a..5b43fd84 100644 --- a/SpotifyAPI/Web/SpotifyWebClient.cs +++ b/SpotifyAPI/Web/SpotifyWebClient.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; +using SpotifyAPI.Web.Models; namespace SpotifyAPI.Web { @@ -30,130 +31,165 @@ namespace SpotifyAPI.Web _webClient.Dispose(); } - public string Download(string url) + public Tuple Download(string url) { - string response; + Tuple response; try { - response = _encoding.GetString(DownloadRaw(url)); + Tuple raw = DownloadRaw(url); + response = new Tuple(raw.Item1, _encoding.GetString(raw.Item2)); } catch (WebException e) { using (StreamReader reader = new StreamReader(e.Response.GetResponseStream())) { - response = reader.ReadToEnd(); + response = new Tuple(new ResponseInfo + { + Headers = _webClient.ResponseHeaders + }, reader.ReadToEnd()); } } return response; } - public async Task DownloadAsync(string url) + public async Task> DownloadAsync(string url) { - string response; + Tuple response; try { - response = _encoding.GetString(await DownloadRawAsync(url)); + Tuple raw = await DownloadRawAsync(url); + response = new Tuple(raw.Item1, _encoding.GetString(raw.Item2)); } catch (WebException e) { using (StreamReader reader = new StreamReader(e.Response.GetResponseStream())) { - response = reader.ReadToEnd(); + response = new Tuple(new ResponseInfo + { + Headers = _webClient.ResponseHeaders + }, reader.ReadToEnd()); } } return response; } - public byte[] DownloadRaw(string url) + public Tuple DownloadRaw(string url) { - return _webClient.DownloadData(url); + byte[] data = _webClient.DownloadData(url); + ResponseInfo info = new ResponseInfo() + { + Headers = _webClient.ResponseHeaders + }; + return new Tuple(info, data); } - public async Task DownloadRawAsync(string url) + public async Task> DownloadRawAsync(string url) { using (WebClient webClient = new WebClient()) { webClient.Proxy = null; webClient.Encoding = _encoding; webClient.Headers = _webClient.Headers; - return await _webClient.DownloadDataTaskAsync(url); + + byte[] data = await _webClient.DownloadDataTaskAsync(url); + ResponseInfo info = new ResponseInfo() + { + Headers = webClient.ResponseHeaders + }; + return new Tuple(info, data); } } - public T DownloadJson(string url) + public Tuple DownloadJson(string url) { - string response = Download(url); - return JsonConvert.DeserializeObject(response, JsonSettings); + Tuple response = Download(url); + return new Tuple(response.Item1, JsonConvert.DeserializeObject(response.Item2, JsonSettings)); } - public async Task DownloadJsonAsync(string url) + public async Task> DownloadJsonAsync(string url) { - string response = await DownloadAsync(url); - return JsonConvert.DeserializeObject(response, JsonSettings); + Tuple response = await DownloadAsync(url); + return new Tuple(response.Item1, JsonConvert.DeserializeObject(response.Item2, JsonSettings)); } - public string Upload(string url, string body, string method) + public Tuple Upload(string url, string body, string method) { - string response; + Tuple response; try { - byte[] data = UploadRaw(url, body, method); - response = _encoding.GetString(data); + Tuple data = UploadRaw(url, body, method); + response = new Tuple(data.Item1, _encoding.GetString(data.Item2)); } catch (WebException e) { using (StreamReader reader = new StreamReader(e.Response.GetResponseStream())) { - response = reader.ReadToEnd(); + response = new Tuple(new ResponseInfo + { + Headers = _webClient.ResponseHeaders + }, reader.ReadToEnd()); } } return response; } - public async Task UploadAsync(string url, string body, string method) + public async Task> UploadAsync(string url, string body, string method) { - string response; + Tuple response; try { - byte[] data = await UploadRawAsync(url, body, method); - response = _encoding.GetString(data); + Tuple data = await UploadRawAsync(url, body, method); + response = new Tuple(data.Item1, _encoding.GetString(data.Item2)); } catch (WebException e) { using (StreamReader reader = new StreamReader(e.Response.GetResponseStream())) { - response = reader.ReadToEnd(); + response = new Tuple(new ResponseInfo + { + Headers = _webClient.ResponseHeaders + }, reader.ReadToEnd()); } } return response; } - public byte[] UploadRaw(string url, string body, string method) + public Tuple UploadRaw(string url, string body, string method) { - return _webClient.UploadData(url, method, _encoding.GetBytes(body)); + 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) + 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; - return await webClient.UploadDataTaskAsync(url, method, _encoding.GetBytes(body)); + byte[] data = await _webClient.UploadDataTaskAsync(url, method, _encoding.GetBytes(body)); + ResponseInfo info = new ResponseInfo + { + Headers = _webClient.ResponseHeaders + }; + return new Tuple(info, data); } } - public T UploadJson(string url, string body, string method) + public Tuple UploadJson(string url, string body, string method) { - string response = Upload(url, body, method); - return JsonConvert.DeserializeObject(response, JsonSettings); + 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) + public async Task> UploadJsonAsync(string url, string body, string method) { - string response = await UploadAsync(url, body, method); - return JsonConvert.DeserializeObject(response, JsonSettings); + Tuple response = await UploadAsync(url, body, method); + return new Tuple(response.Item1, JsonConvert.DeserializeObject(response.Item2, JsonSettings)); } public void SetHeader(string header, string value)