diff --git a/CryptoExchange.Net/Clients/RestApiClient.cs b/CryptoExchange.Net/Clients/RestApiClient.cs index 4f85a04..aa98487 100644 --- a/CryptoExchange.Net/Clients/RestApiClient.cs +++ b/CryptoExchange.Net/Clients/RestApiClient.cs @@ -239,7 +239,7 @@ namespace CryptoExchange.Net.Clients additionalHeaders); _logger.RestApiSendRequest(request.RequestId, definition, request.Content, string.IsNullOrEmpty(request.Uri.Query) ? "-" : request.Uri.Query, string.Join(", ", request.GetHeaders().Select(h => h.Key + $"=[{string.Join(",", h.Value)}]"))); TotalRequestsMade++; - var result = await GetResponseAsync(request, definition.RateLimitGate, cancellationToken).ConfigureAwait(false); + var result = await GetResponseAsync(definition, request, definition.RateLimitGate, cancellationToken).ConfigureAwait(false); if (result.Error is not CancellationRequestedError) { var originalData = OutputOriginalData ? result.OriginalData : "[Data only available when OutputOriginal = true]"; @@ -424,11 +424,13 @@ namespace CryptoExchange.Net.Clients /// /// Executes the request and returns the result deserialized into the type parameter class /// + /// The request definition /// The request object to execute /// The ratelimit gate used /// Cancellation token /// protected virtual async Task> GetResponseAsync( + RequestDefinition requestDefinition, IRequest request, IRateLimitGate? gate, CancellationToken cancellationToken) @@ -448,7 +450,7 @@ namespace CryptoExchange.Net.Clients var outputOriginalData = ApiOptions.OutputOriginalData ?? ClientOptions.OutputOriginalData; accessor = CreateAccessor(); - if (!response.IsSuccessStatusCode) + if (!response.IsSuccessStatusCode && !requestDefinition.TryParseOnNonSuccess) { // Error response var readResult = await accessor.Read(responseStream, true).ConfigureAwait(false); @@ -488,7 +490,7 @@ namespace CryptoExchange.Net.Clients } // Json response received - var parsedError = TryParseError(response.ResponseHeaders, accessor); + var parsedError = TryParseError(requestDefinition, response.ResponseHeaders, accessor); if (parsedError != null) { if (parsedError is ServerRateLimitError rateError) @@ -541,10 +543,11 @@ namespace CryptoExchange.Net.Clients /// This method will be called for each response to be able to check if the response is an error or not. /// If the response is an error this method should return the parsed error, else it should return null /// + /// Request definition /// Data accessor /// The response headers /// Null if not an error, Error otherwise - protected virtual Error? TryParseError(KeyValuePair[] responseHeaders, IMessageAccessor accessor) => null; + protected virtual Error? TryParseError(RequestDefinition requestDefinition, KeyValuePair[] responseHeaders, IMessageAccessor accessor) => null; /// /// Can be used to indicate that a request should be retried. Defaults to false. Make sure to retry a max number of times (based on the the tries parameter) or the request will retry forever. diff --git a/CryptoExchange.Net/Objects/RequestDefinition.cs b/CryptoExchange.Net/Objects/RequestDefinition.cs index 6ce9ab4..429100d 100644 --- a/CryptoExchange.Net/Objects/RequestDefinition.cs +++ b/CryptoExchange.Net/Objects/RequestDefinition.cs @@ -62,6 +62,11 @@ namespace CryptoExchange.Net.Objects /// public bool PreventCaching { get; set; } + /// + /// Whether the response to this requests should attempted to be parsed even when the status indicates failure + /// + public bool TryParseOnNonSuccess { get; set; } + /// /// Connection id /// diff --git a/CryptoExchange.Net/Objects/RequestDefinitionCache.cs b/CryptoExchange.Net/Objects/RequestDefinitionCache.cs index 9c50a0b..1fac34b 100644 --- a/CryptoExchange.Net/Objects/RequestDefinitionCache.cs +++ b/CryptoExchange.Net/Objects/RequestDefinitionCache.cs @@ -46,6 +46,7 @@ namespace CryptoExchange.Net.Objects /// Parameter position /// Array serialization type /// Prevent request caching + /// Try parse the response even when status is not success /// public RequestDefinition GetOrCreate( HttpMethod method, @@ -57,8 +58,9 @@ namespace CryptoExchange.Net.Objects RequestBodyFormat? requestBodyFormat = null, HttpMethodParameterPosition? parameterPosition = null, ArrayParametersSerialization? arraySerialization = null, - bool? preventCaching = null) - => GetOrCreate(method + path, method, path, rateLimitGate, weight, authenticated, limitGuard, requestBodyFormat, parameterPosition, arraySerialization, preventCaching); + bool? preventCaching = null, + bool? tryParseOnNonSuccess = null) + => GetOrCreate(method + path, method, path, rateLimitGate, weight, authenticated, limitGuard, requestBodyFormat, parameterPosition, arraySerialization, preventCaching, tryParseOnNonSuccess); /// /// Get a definition if it is already in the cache or create a new definition and add it to the cache @@ -74,6 +76,7 @@ namespace CryptoExchange.Net.Objects /// Parameter position /// Array serialization type /// Prevent request caching + /// Try parse the response even when status is not success /// public RequestDefinition GetOrCreate( string identifier, @@ -86,7 +89,8 @@ namespace CryptoExchange.Net.Objects RequestBodyFormat? requestBodyFormat = null, HttpMethodParameterPosition? parameterPosition = null, ArrayParametersSerialization? arraySerialization = null, - bool? preventCaching = null) + bool? preventCaching = null, + bool? tryParseOnNonSuccess = null) { if (!_definitions.TryGetValue(identifier, out var def)) @@ -100,7 +104,8 @@ namespace CryptoExchange.Net.Objects ArraySerialization = arraySerialization, RequestBodyFormat = requestBodyFormat, ParameterPosition = parameterPosition, - PreventCaching = preventCaching ?? false + PreventCaching = preventCaching ?? false, + TryParseOnNonSuccess = tryParseOnNonSuccess ?? false }; _definitions.TryAdd(identifier, def); }