diff --git a/CryptoExchange.Net/BaseClient.cs b/CryptoExchange.Net/BaseClient.cs index 6fa01d9..d2c03f2 100644 --- a/CryptoExchange.Net/BaseClient.cs +++ b/CryptoExchange.Net/BaseClient.cs @@ -69,6 +69,13 @@ namespace CryptoExchange.Net /// protected CallResult ValidateJson(string data) { + if (string.IsNullOrEmpty(data)) + { + var info = "Empty data object received"; + log.Write(LogVerbosity.Error, info); + return new CallResult(null, new DeserializeError(info)); + } + try { return new CallResult(JToken.Parse(data), null); diff --git a/CryptoExchange.Net/Interfaces/IResponse.cs b/CryptoExchange.Net/Interfaces/IResponse.cs index 816d920..1b5d9f1 100644 --- a/CryptoExchange.Net/Interfaces/IResponse.cs +++ b/CryptoExchange.Net/Interfaces/IResponse.cs @@ -1,9 +1,11 @@ using System.IO; +using System.Net; namespace CryptoExchange.Net.Interfaces { public interface IResponse { + HttpStatusCode StatusCode { get; } Stream GetResponseStream(); void Close(); } diff --git a/CryptoExchange.Net/Objects/CallResult.cs b/CryptoExchange.Net/Objects/CallResult.cs index af1d214..78eee85 100644 --- a/CryptoExchange.Net/Objects/CallResult.cs +++ b/CryptoExchange.Net/Objects/CallResult.cs @@ -1,4 +1,6 @@ -namespace CryptoExchange.Net.Objects +using System.Net; + +namespace CryptoExchange.Net.Objects { public class CallResult { @@ -21,4 +23,17 @@ Error = error; } } + + public class WebCallResult: CallResult + { + /// + /// The status code of the response. Note that a OK status does not always indicate success, check the Success parameter for this. + /// + public HttpStatusCode? ResponseStatusCode { get; set; } + + public WebCallResult(HttpStatusCode? code, T data, Error error): base(data, error) + { + ResponseStatusCode = code; + } + } } diff --git a/CryptoExchange.Net/Requests/Request.cs b/CryptoExchange.Net/Requests/Request.cs index 721959b..8b64c4f 100644 --- a/CryptoExchange.Net/Requests/Request.cs +++ b/CryptoExchange.Net/Requests/Request.cs @@ -67,7 +67,7 @@ namespace CryptoExchange.Net.Requests public async Task GetResponse() { - return new Response(await request.GetResponseAsync().ConfigureAwait(false)); + return new Response((HttpWebResponse)await request.GetResponseAsync().ConfigureAwait(false)); } } } diff --git a/CryptoExchange.Net/Requests/Response.cs b/CryptoExchange.Net/Requests/Response.cs index a467f47..f8bc037 100644 --- a/CryptoExchange.Net/Requests/Response.cs +++ b/CryptoExchange.Net/Requests/Response.cs @@ -6,9 +6,11 @@ namespace CryptoExchange.Net.Requests { public class Response : IResponse { - private readonly WebResponse response; + private readonly HttpWebResponse response; - public Response(WebResponse response) + public HttpStatusCode StatusCode => response.StatusCode; + + public Response(HttpWebResponse response) { this.response = response; } diff --git a/CryptoExchange.Net/RestClient.cs b/CryptoExchange.Net/RestClient.cs index 13c79f4..8400b2f 100644 --- a/CryptoExchange.Net/RestClient.cs +++ b/CryptoExchange.Net/RestClient.cs @@ -115,13 +115,13 @@ namespace CryptoExchange.Net /// Whether or not the request should be authenticated /// Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug) /// - protected virtual async Task> ExecuteRequest(Uri uri, string method = Constants.GetMethod, Dictionary parameters = null, bool signed = false, bool checkResult = true) where T : class + protected virtual async Task> ExecuteRequest(Uri uri, string method = Constants.GetMethod, Dictionary parameters = null, bool signed = false, bool checkResult = true) where T : class { log.Write(LogVerbosity.Debug, "Creating request for " + uri); if (signed && authProvider == null) { log.Write(LogVerbosity.Warning, $"Request {uri.AbsolutePath} failed because no ApiCredentials were provided"); - return new CallResult(null, new NoApiCredentialsError()); + return new WebCallResult(null, null, new NoApiCredentialsError()); } var request = ConstructRequest(uri, method, parameters, signed); @@ -138,7 +138,7 @@ namespace CryptoExchange.Net if (!limitResult.Success) { log.Write(LogVerbosity.Debug, $"Request {uri.AbsolutePath} failed because of rate limit"); - return new CallResult(null, limitResult.Error); + return new WebCallResult(null, null, limitResult.Error); } if (limitResult.Data > 0) @@ -152,16 +152,20 @@ namespace CryptoExchange.Net log.Write(LogVerbosity.Debug, $"Sending {method}{(signed ? " signed" : "")} request to {request.Uri} {paramString ?? ""}"); var result = await ExecuteRequest(request).ConfigureAwait(false); if(!result.Success) - return new CallResult(null, result.Error); + return new WebCallResult(result.ResponseStatusCode, null, result.Error); var jsonResult = ValidateJson(result.Data); if(!jsonResult.Success) - return new CallResult(null, jsonResult.Error); + return new WebCallResult(result.ResponseStatusCode, null, jsonResult.Error); if (IsErrorResponse(jsonResult.Data)) - return new CallResult(null, ParseErrorResponse(jsonResult.Data)); - - return Deserialize(jsonResult.Data, checkResult); + return new WebCallResult(result.ResponseStatusCode, null, ParseErrorResponse(jsonResult.Data)); + + var desResult = Deserialize(jsonResult.Data, checkResult); + if (!desResult.Success) + return new WebCallResult(result.ResponseStatusCode, null, desResult.Error); + + return new WebCallResult(result.ResponseStatusCode, desResult.Data, null); } /// @@ -258,7 +262,7 @@ namespace CryptoExchange.Net /// /// The request object to execute /// - private async Task> ExecuteRequest(IRequest request) + private async Task> ExecuteRequest(IRequest request) { var returnedData = ""; try @@ -272,12 +276,15 @@ namespace CryptoExchange.Net log.Write(LogVerbosity.Debug, "Data returned: " + returnedData); } + var statusCode = response.StatusCode; response.Close(); - return new CallResult(returnedData, null); + return new WebCallResult(statusCode, returnedData, null); } catch (WebException we) { var response = (HttpWebResponse)we.Response; + var statusCode = response?.StatusCode; + try { using (var reader = new StreamReader(response.GetResponseStream())) @@ -289,7 +296,7 @@ namespace CryptoExchange.Net response.Close(); var jsonResult = ValidateJson(returnedData); - return !jsonResult.Success ? new CallResult(null, jsonResult.Error) : new CallResult(null, ParseErrorResponse(jsonResult.Data)); + return !jsonResult.Success ? new WebCallResult(statusCode, null, jsonResult.Error) : new WebCallResult(statusCode, null, ParseErrorResponse(jsonResult.Data)); } catch (Exception) { @@ -300,18 +307,18 @@ namespace CryptoExchange.Net { infoMessage += $" | {we.Status} - {we.Message}"; log.Write(LogVerbosity.Warning, infoMessage); - return new CallResult(null, new WebError(infoMessage)); + return new WebCallResult(0, null, new WebError(infoMessage)); } infoMessage = $"Status: {response.StatusCode}-{response.StatusDescription}, Message: {we.Message}"; log.Write(LogVerbosity.Warning, infoMessage); response.Close(); - return new CallResult(null, new ServerError(infoMessage)); + return new WebCallResult(statusCode, null, new ServerError(infoMessage)); } catch (Exception e) { log.Write(LogVerbosity.Error, $"Unknown error occured: {e.GetType()}, {e.Message}, {e.StackTrace}"); - return new CallResult(null, new UnknownError(e.Message + ", data: " + returnedData)); + return new WebCallResult(null, null, new UnknownError(e.Message + ", data: " + returnedData)); } }