From b95215866bc5b7ff0bdca47d98efcb51732c4b16 Mon Sep 17 00:00:00 2001 From: Jan Korf Date: Tue, 2 Jun 2020 19:32:55 +0200 Subject: [PATCH] Added requestBodyEmptyContent setting for rest client, Added TryParseError for rest implementations to check for error with success status code --- CryptoExchange.Net/CryptoExchange.Net.csproj | 4 +- CryptoExchange.Net/CryptoExchange.Net.xml | 156 +++++++++++++++++++ CryptoExchange.Net/RestClient.cs | 53 ++++++- README.md | 4 + 4 files changed, 210 insertions(+), 7 deletions(-) diff --git a/CryptoExchange.Net/CryptoExchange.Net.csproj b/CryptoExchange.Net/CryptoExchange.Net.csproj index b8e461c..279d669 100644 --- a/CryptoExchange.Net/CryptoExchange.Net.csproj +++ b/CryptoExchange.Net/CryptoExchange.Net.csproj @@ -6,12 +6,12 @@ CryptoExchange.Net JKorf A base package for implementing cryptocurrency exchange API's - 3.0.7 + 3.0.8 false https://github.com/JKorf/CryptoExchange.Net en true - 3.0.7 - Added error debug output, Fix for unsubscribe causing possible deadlock + 3.0.8 - Added empty body content setting, added TryParseError virtual method enable 8.0 MIT diff --git a/CryptoExchange.Net/CryptoExchange.Net.xml b/CryptoExchange.Net/CryptoExchange.Net.xml index d5c70b9..aa6e171 100644 --- a/CryptoExchange.Net/CryptoExchange.Net.xml +++ b/CryptoExchange.Net/CryptoExchange.Net.xml @@ -2112,6 +2112,11 @@ Request body content type + + + Whether or not we need to manually parse an error instead of relying on the http status code + + How to serialize array parameters @@ -2188,6 +2193,14 @@ Cancellation token + + + Can be used to parse an error even though response status indicates success. Some apis always return 200 OK, even though there is an error. + This can be used together with ManualParseError to check if it is an error before deserializing to an object + + Received data + Null if not an error, Error otherwise + Creates a request object @@ -2926,5 +2939,148 @@ + + + Specifies that is allowed as an input even if the + corresponding type disallows it. + + + + + Initializes a new instance of the class. + + + + + Specifies that is disallowed as an input even if the + corresponding type allows it. + + + + + Initializes a new instance of the class. + + + + + Specifies that a method that will never return under any circumstance. + + + + + Initializes a new instance of the class. + + + + + Specifies that the method will not return if the associated + parameter is passed the specified value. + + + + + Gets the condition parameter value. + Code after the method is considered unreachable by diagnostics if the argument + to the associated parameter matches this value. + + + + + Initializes a new instance of the + class with the specified parameter value. + + + The condition parameter value. + Code after the method is considered unreachable by diagnostics if the argument + to the associated parameter matches this value. + + + + + Specifies that an output may be even if the + corresponding type disallows it. + + + + + Initializes a new instance of the class. + + + + + Specifies that when a method returns , + the parameter may be even if the corresponding type disallows it. + + + + + Gets the return value condition. + If the method returns this value, the associated parameter may be . + + + + + Initializes the attribute with the specified return value condition. + + + The return value condition. + If the method returns this value, the associated parameter may be . + + + + + Specifies that an output is not even if the + corresponding type allows it. + + + + + Initializes a new instance of the class. + + + + + Specifies that the output will be non- if the + named parameter is non-. + + + + + Gets the associated parameter name. + The output will be non- if the argument to the + parameter specified is non-. + + + + + Initializes the attribute with the associated parameter name. + + + The associated parameter name. + The output will be non- if the argument to the + parameter specified is non-. + + + + + Specifies that when a method returns , + the parameter will not be even if the corresponding type allows it. + + + + + Gets the return value condition. + If the method returns this value, the associated parameter will not be . + + + + + Initializes the attribute with the specified return value condition. + + + The return value condition. + If the method returns this value, the associated parameter will not be . + + diff --git a/CryptoExchange.Net/RestClient.cs b/CryptoExchange.Net/RestClient.cs index a36e3b7..42043c4 100644 --- a/CryptoExchange.Net/RestClient.cs +++ b/CryptoExchange.Net/RestClient.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Net.Http; using System.Net.NetworkInformation; using System.Net.Sockets; +using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using System.Web; @@ -39,11 +40,21 @@ namespace CryptoExchange.Net /// protected RequestBodyFormat requestBodyFormat = RequestBodyFormat.Json; + /// + /// Whether or not we need to manually parse an error instead of relying on the http status code + /// + protected bool manualParseError = false; + /// /// How to serialize array parameters /// protected ArrayParametersSerialization arraySerialization = ArrayParametersSerialization.Array; + /// + /// What request body should be when no data is send + /// + protected string requestBodyEmptyContent = "{}"; + /// /// Timeout for requests /// @@ -205,11 +216,32 @@ namespace CryptoExchange.Net var responseStream = await response.GetResponseStream().ConfigureAwait(false); if (response.IsSuccessStatusCode) { - var desResult = await Deserialize(responseStream).ConfigureAwait(false); - responseStream.Close(); - response.Close(); + if (manualParseError) + { + using var reader = new StreamReader(responseStream); + var data = await reader.ReadToEndAsync().ConfigureAwait(false); + responseStream.Close(); + response.Close(); + log.Write(LogVerbosity.Debug, $"Data received: {data}"); - return new WebCallResult(statusCode, headers, desResult.Data, desResult.Error); + var parseResult = ValidateJson(data); + if (!parseResult.Success) + return WebCallResult.CreateErrorResult(response.StatusCode, response.ResponseHeaders, new ServerError(data)); + var error = await TryParseError(parseResult.Data); + if(error != null) + return WebCallResult.CreateErrorResult(response.StatusCode, response.ResponseHeaders, error); + + var deserializeResult = Deserialize(parseResult.Data); + return new WebCallResult(response.StatusCode, response.ResponseHeaders, deserializeResult.Data, deserializeResult.Error); + } + else + { + var desResult = await Deserialize(responseStream).ConfigureAwait(false); + responseStream.Close(); + response.Close(); + + return new WebCallResult(statusCode, headers, desResult.Data, desResult.Error); + } } else { @@ -244,6 +276,17 @@ namespace CryptoExchange.Net } } + /// + /// Can be used to parse an error even though response status indicates success. Some apis always return 200 OK, even though there is an error. + /// This can be used together with ManualParseError to check if it is an error before deserializing to an object + /// + /// Received data + /// Null if not an error, Error otherwise + protected virtual Task TryParseError(JToken data) + { + return Task.FromResult(null); + } + /// /// Creates a request object /// @@ -280,7 +323,7 @@ namespace CryptoExchange.Net if(parameters?.Any() == true) WriteParamBody(request, parameters, contentType); else - request.SetContent("{}", contentType); + request.SetContent(requestBodyEmptyContent, contentType); } return request; diff --git a/README.md b/README.md index 614612c..2726beb 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,10 @@ The order book will automatically reconnect when the connection is lost and resy To stop synchronizing an order book use the `Stop` method. ## Release notes +* Version 3.0.8 - 02 Jun 2020 + * Added requestBodyEmptyContent setting for rest client + * Added TryParseError for rest implementations to check for error with success status code + * Version 3.0.7 - 20 May 2020 * Added error debug output * Fix for unsubscribe causing possible deadlock