diff --git a/CryptoExchange.Net/BaseClient.cs b/CryptoExchange.Net/BaseClient.cs
index b01e1a1..8088790 100644
--- a/CryptoExchange.Net/BaseClient.cs
+++ b/CryptoExchange.Net/BaseClient.cs
@@ -36,6 +36,10 @@ namespace CryptoExchange.Net
/// The auth provider
///
protected internal AuthenticationProvider? authProvider;
+ ///
+ /// Should check objects for missing properties based on the model and the received JSON
+ ///
+ public bool ShouldCheckObjects { get; set; }
///
/// The last used id
@@ -73,6 +77,7 @@ namespace CryptoExchange.Net
apiProxy = options.Proxy;
log.Write(LogVerbosity.Debug, $"Client configuration: {options}");
+ ShouldCheckObjects = options.ShouldCheckObjects;
}
///
@@ -127,8 +132,9 @@ namespace CryptoExchange.Net
/// The data to deserialize
/// Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)
/// A specific serializer to use
+ /// Id of the request
///
- protected CallResult Deserialize(string data, bool checkObject = true, JsonSerializer? serializer = null)
+ protected CallResult Deserialize(string data, bool? checkObject = null, JsonSerializer? serializer = null, int? requestId = null)
{
var tokenResult = ValidateJson(data);
if (!tokenResult)
@@ -137,7 +143,7 @@ namespace CryptoExchange.Net
return new CallResult(default, tokenResult.Error);
}
- return Deserialize(tokenResult.Data, checkObject, serializer);
+ return Deserialize(tokenResult.Data, checkObject, serializer, requestId);
}
///
@@ -148,30 +154,30 @@ namespace CryptoExchange.Net
/// Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)
/// A specific serializer to use
///
- protected CallResult Deserialize(JToken obj, bool checkObject = true, JsonSerializer? serializer = null)
+ protected CallResult Deserialize(JToken obj, bool? checkObject = null, JsonSerializer? serializer = null, int? requestId = null)
{
if (serializer == null)
serializer = defaultSerializer;
try
{
- if (checkObject && log.Level == LogVerbosity.Debug)
+ if ((checkObject ?? ShouldCheckObjects)&& log.Level == LogVerbosity.Debug)
{
try
{
if (obj is JObject o)
{
- CheckObject(typeof(T), o);
+ CheckObject(typeof(T), o, requestId);
}
else if (obj is JArray j)
{
if (j.HasValues && j[0] is JObject jObject)
- CheckObject(typeof(T).GetElementType(), jObject);
+ CheckObject(typeof(T).GetElementType(), jObject, requestId);
}
}
catch (Exception e)
{
- log.Write(LogVerbosity.Debug, "Failed to check response data: " + e.Message);
+ log.Write(LogVerbosity.Debug, $"{(requestId != null ? $"[{ requestId}] " : "")}Failed to check response data: " + e.Message);
}
}
@@ -179,19 +185,19 @@ namespace CryptoExchange.Net
}
catch (JsonReaderException jre)
{
- var info = $"Deserialize JsonReaderException: {jre.Message}, Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}. Received data: {obj}";
+ var info = $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize JsonReaderException: {jre.Message}, Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}. Received data: {obj}";
log.Write(LogVerbosity.Error, info);
return new CallResult(default, new DeserializeError(info));
}
catch (JsonSerializationException jse)
{
- var info = $"Deserialize JsonSerializationException: {jse.Message}. Received data: {obj}";
+ var info = $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize JsonSerializationException: {jse.Message}. Received data: {obj}";
log.Write(LogVerbosity.Error, info);
return new CallResult(default, new DeserializeError(info));
}
catch (Exception ex)
{
- var info = $"Deserialize Unknown Exception: {ex.Message}. Received data: {obj}";
+ var info = $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize Unknown Exception: {ex.Message}. Received data: {obj}";
log.Write(LogVerbosity.Error, info);
return new CallResult(default, new DeserializeError(info));
}
@@ -203,8 +209,9 @@ namespace CryptoExchange.Net
/// The type to deserialize into
/// The stream to deserialize
/// A specific serializer to use
+ /// Id of the request
///
- protected async Task> Deserialize(Stream stream, JsonSerializer? serializer = null)
+ protected async Task> Deserialize(Stream stream, JsonSerializer? serializer = null, int? requestId = null)
{
if (serializer == null)
serializer = defaultSerializer;
@@ -215,8 +222,8 @@ namespace CryptoExchange.Net
if (log.Level == LogVerbosity.Debug)
{
var data = await reader.ReadToEndAsync().ConfigureAwait(false);
- log.Write(LogVerbosity.Debug, $"Data received: {data}");
- return Deserialize(data);
+ log.Write(LogVerbosity.Debug, $"{(requestId != null ? $"[{requestId}] ": "")}Data received: {data}");
+ return Deserialize(data, null, serializer, requestId);
}
using var jsonReader = new JsonTextReader(reader);
@@ -227,7 +234,7 @@ namespace CryptoExchange.Net
if(stream.CanSeek)
stream.Seek(0, SeekOrigin.Begin);
var data = await ReadStream(stream).ConfigureAwait(false);
- log.Write(LogVerbosity.Error, $"Deserialize JsonReaderException: {jre.Message}, Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}, data: {data}");
+ log.Write(LogVerbosity.Error, $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize JsonReaderException: {jre.Message}, Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}, data: {data}");
return new CallResult(default, new DeserializeError(data));
}
catch (JsonSerializationException jse)
@@ -235,7 +242,7 @@ namespace CryptoExchange.Net
if (stream.CanSeek)
stream.Seek(0, SeekOrigin.Begin);
var data = await ReadStream(stream).ConfigureAwait(false);
- log.Write(LogVerbosity.Error, $"Deserialize JsonSerializationException: {jse.Message}, data: {data}");
+ log.Write(LogVerbosity.Error, $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize JsonSerializationException: {jse.Message}, data: {data}");
return new CallResult(default, new DeserializeError(data));
}
catch (Exception ex)
@@ -243,7 +250,7 @@ namespace CryptoExchange.Net
if (stream.CanSeek)
stream.Seek(0, SeekOrigin.Begin);
var data = await ReadStream(stream).ConfigureAwait(false);
- log.Write(LogVerbosity.Error, $"Deserialize Unknown Exception: {ex.Message}, data: {data}");
+ log.Write(LogVerbosity.Error, $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize Unknown Exception: {ex.Message}, data: {data}");
return new CallResult(default, new DeserializeError(data));
}
}
@@ -254,8 +261,11 @@ namespace CryptoExchange.Net
return await reader.ReadToEndAsync().ConfigureAwait(false);
}
- private void CheckObject(Type type, JObject obj)
+ private void CheckObject(Type type, JObject obj, int? requestId = null)
{
+ if (type == null)
+ return;
+
if (type.GetCustomAttribute(true) != null)
// If type has a custom JsonConverter we assume this will handle property mapping
return;
@@ -265,7 +275,7 @@ namespace CryptoExchange.Net
if (!obj.HasValues && type != typeof(object))
{
- log.Write(LogVerbosity.Warning, $"Expected `{type.Name}`, but received object was empty");
+ log.Write(LogVerbosity.Warning, $"{(requestId != null ? $"[{requestId}] " : "")}Expected `{type.Name}`, but received object was empty");
return;
}
@@ -291,7 +301,7 @@ namespace CryptoExchange.Net
{
if (!(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>)))
{
- log.Write(LogVerbosity.Warning, $"Local object doesn't have property `{token.Key}` expected in type `{type.Name}`");
+ log.Write(LogVerbosity.Warning, $"{(requestId != null ? $"[{requestId}] " : "")}Local object doesn't have property `{token.Key}` expected in type `{type.Name}`");
isDif = true;
}
continue;
@@ -306,9 +316,9 @@ namespace CryptoExchange.Net
if (!IsSimple(propType) && propType != typeof(DateTime))
{
if (propType.IsArray && token.Value.HasValues && ((JArray)token.Value).Any() && ((JArray)token.Value)[0] is JObject)
- CheckObject(propType.GetElementType(), (JObject)token.Value[0]);
+ CheckObject(propType.GetElementType(), (JObject)token.Value[0], requestId);
else if (token.Value is JObject o)
- CheckObject(propType, o);
+ CheckObject(propType, o, requestId);
}
}
@@ -321,11 +331,11 @@ namespace CryptoExchange.Net
continue;
isDif = true;
- log.Write(LogVerbosity.Warning, $"Local object has property `{prop}` but was not found in received object of type `{type.Name}`");
+ log.Write(LogVerbosity.Warning, $"{(requestId != null ? $"[{requestId}] " : "")}Local object has property `{prop}` but was not found in received object of type `{type.Name}`");
}
if (isDif)
- log.Write(LogVerbosity.Debug, "Returned data: " + obj);
+ log.Write(LogVerbosity.Debug, $"{(requestId != null ? $"[{ requestId}] " : "")}Returned data: " + obj);
}
private static PropertyInfo? GetProperty(string name, IEnumerable props)
diff --git a/CryptoExchange.Net/CryptoExchange.Net.xml b/CryptoExchange.Net/CryptoExchange.Net.xml
index 7995e9f..a171b30 100644
--- a/CryptoExchange.Net/CryptoExchange.Net.xml
+++ b/CryptoExchange.Net/CryptoExchange.Net.xml
@@ -224,6 +224,11 @@
The auth provider
+
+
+ Should check objects for missing properties based on the model and the received JSON
+
+
The last used id
@@ -259,7 +264,7 @@
The data to parse
-
+
Deserialize a string into an object
@@ -267,9 +272,10 @@
The data to deserialize
Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)
A specific serializer to use
+ Id of the request
-
+
Deserialize a JToken into an object
@@ -279,13 +285,14 @@
A specific serializer to use
-
+
Deserialize a stream into an object
The type to deserialize into
The stream to deserialize
A specific serializer to use
+ Id of the request
@@ -588,6 +595,11 @@
Uri
+
+
+ internal request id for tracing
+
+
Set byte content
@@ -620,20 +632,22 @@
Request factory interface
-
+
Create a request for an uri
+
-
+
Configure the requests created by this factory
Request timeout to use
- Proxy settings to use
+ Proxy settings to use
+ Optional shared http client instance
@@ -1582,6 +1596,11 @@
The api credentials
+
+
+ Should check objects for missing properties based on the model and the received JSON
+
+
Proxy to use
@@ -1616,11 +1635,23 @@
The time the server has to respond to a request before timing out
+
+
+ Http client to use. If a HttpClient is provided in this property the RequestTimeout and Proxy options will be ignored and should be set on the provided HttpClient instance
+
+
ctor
-
+ The base address of the API
+
+
+
+ ctor
+
+ The base address of the API
+ Shared http client instance
@@ -2024,12 +2055,13 @@
Request object
-
+
Create request object for web request
-
+
+
@@ -2043,6 +2075,9 @@
+
+
+
@@ -2060,10 +2095,10 @@
WebRequest factory
-
+
-
+
@@ -2189,7 +2224,7 @@
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)
Where the post parameters should be placed
- How array paramters should be serialized
+ How array parameters should be serialized
@@ -2208,7 +2243,7 @@
Received data
Null if not an error, Error otherwise
-
+
Creates a request object
@@ -2217,7 +2252,8 @@
The parameters of the request
Whether or not the request should be authenticated
Where the post parameters should be placed
- How array paramters should be serialized
+ How array parameters should be serialized
+ Unique id of a request
diff --git a/CryptoExchange.Net/Interfaces/IRequest.cs b/CryptoExchange.Net/Interfaces/IRequest.cs
index 9e819cb..1544436 100644
--- a/CryptoExchange.Net/Interfaces/IRequest.cs
+++ b/CryptoExchange.Net/Interfaces/IRequest.cs
@@ -27,6 +27,10 @@ namespace CryptoExchange.Net.Interfaces
///
Uri Uri { get; }
///
+ /// internal request id for tracing
+ ///
+ int RequestId { get; }
+ ///
/// Set byte content
///
///
diff --git a/CryptoExchange.Net/Interfaces/IRequestFactory.cs b/CryptoExchange.Net/Interfaces/IRequestFactory.cs
index ceb7f8c..2779d92 100644
--- a/CryptoExchange.Net/Interfaces/IRequestFactory.cs
+++ b/CryptoExchange.Net/Interfaces/IRequestFactory.cs
@@ -14,14 +14,16 @@ namespace CryptoExchange.Net.Interfaces
///
///
///
+ ///
///
- IRequest Create(HttpMethod method, string uri);
+ IRequest Create(HttpMethod method, string uri, int requestId);
///
/// Configure the requests created by this factory
///
/// Request timeout to use
- /// Proxy settings to use
- void Configure(TimeSpan requestTimeout, ApiProxy? proxy);
+ /// Proxy settings to use
+ /// Optional shared http client instance
+ void Configure(TimeSpan requestTimeout, ApiProxy? proxy, HttpClient? httpClient=null);
}
}
diff --git a/CryptoExchange.Net/Objects/Options.cs b/CryptoExchange.Net/Objects/Options.cs
index 75bca6c..cc3cd9b 100644
--- a/CryptoExchange.Net/Objects/Options.cs
+++ b/CryptoExchange.Net/Objects/Options.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Net.Http;
using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Logging;
@@ -85,6 +86,10 @@ namespace CryptoExchange.Net.Objects
///
public ApiCredentials? ApiCredentials { get; set; }
+ ///
+ /// Should check objects for missing properties based on the model and the received JSON
+ ///
+ public bool ShouldCheckObjects { get; set; } = true;
///
/// Proxy to use
@@ -127,14 +132,27 @@ namespace CryptoExchange.Net.Objects
///
public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30);
+ ///
+ /// Http client to use. If a HttpClient is provided in this property the RequestTimeout and Proxy options will be ignored and should be set on the provided HttpClient instance
+ ///
+ public HttpClient? HttpClient { get; set; }
+
///
/// ctor
///
- ///
+ /// The base address of the API
public RestClientOptions(string baseAddress): base(baseAddress)
{
}
-
+ ///
+ /// ctor
+ ///
+ /// The base address of the API
+ /// Shared http client instance
+ public RestClientOptions(HttpClient httpClient, string baseAddress) : base(baseAddress)
+ {
+ HttpClient = httpClient;
+ }
///
/// Create a copy of the options
///
@@ -150,7 +168,8 @@ namespace CryptoExchange.Net.Objects
LogWriters = LogWriters,
RateLimiters = RateLimiters,
RateLimitingBehaviour = RateLimitingBehaviour,
- RequestTimeout = RequestTimeout
+ RequestTimeout = RequestTimeout,
+ HttpClient = HttpClient
};
if (ApiCredentials != null)
diff --git a/CryptoExchange.Net/Requests/Request.cs b/CryptoExchange.Net/Requests/Request.cs
index 5c4530c..b0b669a 100644
--- a/CryptoExchange.Net/Requests/Request.cs
+++ b/CryptoExchange.Net/Requests/Request.cs
@@ -1,4 +1,5 @@
using System;
+using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
@@ -20,11 +21,13 @@ namespace CryptoExchange.Net.Requests
/// Create request object for web request
///
///
- ///
- public Request(HttpRequestMessage request, HttpClient client)
+ ///
+ ///
+ public Request(HttpRequestMessage request, HttpClient client, int requestId)
{
httpClient = client;
this.request = request;
+ RequestId = requestId;
}
///
@@ -45,6 +48,8 @@ namespace CryptoExchange.Net.Requests
///
public Uri Uri => request.RequestUri;
+ ///
+ public int RequestId { get; }
///
public void SetContent(string data, string contentType)
diff --git a/CryptoExchange.Net/Requests/RequestFactory.cs b/CryptoExchange.Net/Requests/RequestFactory.cs
index 2434ca1..be68097 100644
--- a/CryptoExchange.Net/Requests/RequestFactory.cs
+++ b/CryptoExchange.Net/Requests/RequestFactory.cs
@@ -11,30 +11,37 @@ namespace CryptoExchange.Net.Requests
///
public class RequestFactory : IRequestFactory
{
- private HttpClient? httpClient;
+ private HttpClient? httpClient;
///
- public void Configure(TimeSpan requestTimeout, ApiProxy? proxy)
+ public void Configure(TimeSpan requestTimeout, ApiProxy? proxy, HttpClient? client = null)
{
- HttpMessageHandler handler = new HttpClientHandler()
+ if (client == null)
{
- Proxy = proxy == null ? null : new WebProxy
+ HttpMessageHandler handler = new HttpClientHandler()
{
- Address = new Uri($"{proxy.Host}:{proxy.Port}"),
- Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password)
- }
- };
+ Proxy = proxy == null ? null : new WebProxy
+ {
+ Address = new Uri($"{proxy.Host}:{proxy.Port}"),
+ Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password)
+ }
+ };
- httpClient = new HttpClient(handler) {Timeout = requestTimeout};
+ httpClient = new HttpClient(handler) { Timeout = requestTimeout };
+ }
+ else
+ {
+ httpClient = client;
+ }
}
///
- public IRequest Create(HttpMethod method, string uri)
+ public IRequest Create(HttpMethod method, string uri, int requestId)
{
if (httpClient == null)
throw new InvalidOperationException("Cant create request before configuring http client");
- return new Request(new HttpRequestMessage(method, uri), httpClient);
+ return new Request(new HttpRequestMessage(method, uri), httpClient, requestId);
}
}
}
diff --git a/CryptoExchange.Net/RestClient.cs b/CryptoExchange.Net/RestClient.cs
index d3a0f55..11b07d8 100644
--- a/CryptoExchange.Net/RestClient.cs
+++ b/CryptoExchange.Net/RestClient.cs
@@ -24,13 +24,13 @@ namespace CryptoExchange.Net
///
/// Base rest client
///
- public abstract class RestClient: BaseClient, IRestClient
+ public abstract class RestClient : BaseClient, IRestClient
{
///
/// The factory for creating requests. Used for unit testing
///
public IRequestFactory RequestFactory { get; set; } = new RequestFactory();
-
+
///
/// Where to place post parameters
///
@@ -77,13 +77,13 @@ namespace CryptoExchange.Net
///
///
///
- protected RestClient(RestClientOptions exchangeOptions, AuthenticationProvider? authenticationProvider): base(exchangeOptions, authenticationProvider)
+ protected RestClient(RestClientOptions exchangeOptions, AuthenticationProvider? authenticationProvider) : base(exchangeOptions, authenticationProvider)
{
if (exchangeOptions == null)
throw new ArgumentNullException(nameof(exchangeOptions));
RequestTimeout = exchangeOptions.RequestTimeout;
- RequestFactory.Configure(exchangeOptions.RequestTimeout, exchangeOptions.Proxy);
+ RequestFactory.Configure(exchangeOptions.RequestTimeout, exchangeOptions.Proxy, exchangeOptions.HttpClient);
RateLimitBehaviour = exchangeOptions.RateLimitingBehaviour;
var rateLimiters = new List();
foreach (var rateLimiter in exchangeOptions.RateLimiters)
@@ -134,10 +134,10 @@ namespace CryptoExchange.Net
{
reply = await ping.SendPingAsync(uri.Host).ConfigureAwait(false);
}
- catch(PingException e)
+ catch (PingException e)
{
if (e.InnerException == null)
- return new CallResult(0, new CantConnectError {Message = "Ping failed: " + e.Message});
+ return new CallResult(0, new CantConnectError { Message = "Ping failed: " + e.Message });
if (e.InnerException is SocketException exception)
return new CallResult(0, new CantConnectError { Message = "Ping failed: " + exception.SocketErrorCode });
@@ -149,7 +149,7 @@ namespace CryptoExchange.Net
ping.Dispose();
}
- if(ct.IsCancellationRequested)
+ if (ct.IsCancellationRequested)
return new CallResult(0, new CancellationRequestedError());
return reply.Status == IPStatus.Success ? new CallResult(reply.RoundtripTime, null) : new CallResult(0, new CantConnectError { Message = "Ping failed: " + reply.Status });
@@ -166,38 +166,39 @@ 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)
/// Where the post parameters should be placed
- /// How array paramters should be serialized
+ /// How array parameters should be serialized
///
[return: NotNull]
protected virtual async Task> SendRequest(Uri uri, HttpMethod method, CancellationToken cancellationToken,
Dictionary? parameters = null, bool signed = false, bool checkResult = true, PostParameters? postPosition = null, ArrayParametersSerialization? arraySerialization = null) where T : class
{
- log.Write(LogVerbosity.Debug, "Creating request for " + uri);
+ var requestId = NextId();
+ log.Write(LogVerbosity.Debug, $"[{requestId}] Creating request for " + uri);
if (signed && authProvider == null)
- {
- log.Write(LogVerbosity.Warning, $"Request {uri.AbsolutePath} failed because no ApiCredentials were provided");
+ {
+ log.Write(LogVerbosity.Warning, $"[{requestId}] Request {uri.AbsolutePath} failed because no ApiCredentials were provided");
return new WebCallResult(null, null, null, new NoApiCredentialsError());
}
- var request = ConstructRequest(uri, method, parameters, signed, postPosition ?? postParametersPosition, arraySerialization ?? this.arraySerialization);
+ var request = ConstructRequest(uri, method, parameters, signed, postPosition ?? postParametersPosition, arraySerialization ?? this.arraySerialization, requestId);
foreach (var limiter in RateLimiters)
{
var limitResult = limiter.LimitRequest(this, uri.AbsolutePath, RateLimitBehaviour);
if (!limitResult.Success)
{
- log.Write(LogVerbosity.Debug, $"Request {uri.AbsolutePath} failed because of rate limit");
+ log.Write(LogVerbosity.Debug, $"[{requestId}] Request {uri.AbsolutePath} failed because of rate limit");
return new WebCallResult(null, null, null, limitResult.Error);
}
if (limitResult.Data > 0)
- log.Write(LogVerbosity.Debug, $"Request {uri.AbsolutePath} was limited by {limitResult.Data}ms by {limiter.GetType().Name}");
+ log.Write(LogVerbosity.Debug, $"[{requestId}] Request {uri.AbsolutePath} was limited by {limitResult.Data}ms by {limiter.GetType().Name}");
}
- string? paramString = null;
- if (method == HttpMethod.Post)
+ string? paramString = null;
+ if (method == HttpMethod.Post)
paramString = " with request body " + request.Content;
- log.Write(LogVerbosity.Debug, $"Sending {method}{(signed ? " signed" : "")} request to {request.Uri}{paramString ?? " "}{(apiProxy == null? "": $" via proxy {apiProxy.Host}")}");
+ log.Write(LogVerbosity.Debug, $"[{requestId}] Sending {method}{(signed ? " signed" : "")} request to {request.Uri}{paramString ?? " "}{(apiProxy == null ? "" : $" via proxy {apiProxy.Host}")}");
return await GetResponse(request, cancellationToken).ConfigureAwait(false);
}
@@ -224,21 +225,21 @@ namespace CryptoExchange.Net
var data = await reader.ReadToEndAsync().ConfigureAwait(false);
responseStream.Close();
response.Close();
- log.Write(LogVerbosity.Debug, $"Data received: {data}");
+ log.Write(LogVerbosity.Debug, $"[{request.RequestId}] Data received: {data}");
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)
+ if (error != null)
return WebCallResult.CreateErrorResult(response.StatusCode, response.ResponseHeaders, error);
- var deserializeResult = Deserialize(parseResult.Data);
+ var deserializeResult = Deserialize(parseResult.Data, null, null, request.RequestId);
return new WebCallResult(response.StatusCode, response.ResponseHeaders, deserializeResult.Data, deserializeResult.Error);
}
else
{
- var desResult = await Deserialize(responseStream).ConfigureAwait(false);
+ var desResult = await Deserialize(responseStream, null, request.RequestId).ConfigureAwait(false);
responseStream.Close();
response.Close();
@@ -249,31 +250,31 @@ namespace CryptoExchange.Net
{
using var reader = new StreamReader(responseStream);
var data = await reader.ReadToEndAsync().ConfigureAwait(false);
- log.Write(LogVerbosity.Debug, $"Error received: {data}");
+ log.Write(LogVerbosity.Debug, $"[{request.RequestId}] Error received: {data}");
responseStream.Close();
response.Close();
var parseResult = ValidateJson(data);
- return new WebCallResult(statusCode, headers, default, parseResult.Success ? ParseErrorResponse(parseResult.Data) :new ServerError(data));
+ return new WebCallResult(statusCode, headers, default, parseResult.Success ? ParseErrorResponse(parseResult.Data) : new ServerError(data));
}
}
catch (HttpRequestException requestException)
{
- log.Write(LogVerbosity.Warning, "Request exception: " + requestException.Message);
+ log.Write(LogVerbosity.Warning, $"[{request.RequestId}] Request exception: " + requestException.Message);
return new WebCallResult(null, null, default, new ServerError(requestException.Message));
}
catch (TaskCanceledException canceledException)
{
- if(canceledException.CancellationToken == cancellationToken)
+ if (canceledException.CancellationToken == cancellationToken)
{
// Cancellation token cancelled
- log.Write(LogVerbosity.Warning, "Request cancel requested");
+ log.Write(LogVerbosity.Warning, $"[{request.RequestId}] Request cancel requested");
return new WebCallResult(null, null, default, new CancellationRequestedError());
}
else
{
// Request timed out
- log.Write(LogVerbosity.Warning, "Request timed out");
- return new WebCallResult(null, null, default, new WebError("Request timed out"));
+ log.Write(LogVerbosity.Warning, $"[{request.RequestId}] Request timed out");
+ return new WebCallResult(null, null, default, new WebError($"[{request.RequestId}] Request timed out"));
}
}
}
@@ -297,22 +298,23 @@ namespace CryptoExchange.Net
/// The parameters of the request
/// Whether or not the request should be authenticated
/// Where the post parameters should be placed
- /// How array paramters should be serialized
+ /// How array parameters should be serialized
+ /// Unique id of a request
///
- protected virtual IRequest ConstructRequest(Uri uri, HttpMethod method, Dictionary? parameters, bool signed, PostParameters postPosition, ArrayParametersSerialization arraySerialization)
+ protected virtual IRequest ConstructRequest(Uri uri, HttpMethod method, Dictionary? parameters, bool signed, PostParameters postPosition, ArrayParametersSerialization arraySerialization, int requestId)
{
if (parameters == null)
parameters = new Dictionary();
var uriString = uri.ToString();
- if(authProvider != null)
+ if (authProvider != null)
parameters = authProvider.AddAuthenticationToParameters(uriString, method, parameters, signed, postPosition, arraySerialization);
- if((method == HttpMethod.Get || method == HttpMethod.Delete || postPosition == PostParameters.InUri) && parameters?.Any() == true)
+ if ((method == HttpMethod.Get || method == HttpMethod.Delete || postPosition == PostParameters.InUri) && parameters?.Any() == true)
uriString += "?" + parameters.CreateParamString(true, arraySerialization);
var contentType = requestBodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
- var request = RequestFactory.Create(method, uriString);
+ var request = RequestFactory.Create(method, uriString, requestId);
request.Accept = Constants.JsonContentHeader;
var headers = new Dictionary();
@@ -324,7 +326,7 @@ namespace CryptoExchange.Net
if ((method == HttpMethod.Post || method == HttpMethod.Put) && postPosition != PostParameters.InUri)
{
- if(parameters?.Any() == true)
+ if (parameters?.Any() == true)
WriteParamBody(request, parameters, contentType);
else
request.SetContent(requestBodyEmptyContent, contentType);
@@ -346,7 +348,7 @@ namespace CryptoExchange.Net
var stringData = JsonConvert.SerializeObject(parameters.OrderBy(p => p.Key).ToDictionary(p => p.Key, p => p.Value));
request.SetContent(stringData, contentType);
}
- else if(requestBodyFormat == RequestBodyFormat.FormData)
+ else if (requestBodyFormat == RequestBodyFormat.FormData)
{
var formData = HttpUtility.ParseQueryString(string.Empty);
foreach (var kvp in parameters.OrderBy(p => p.Key))
@@ -354,7 +356,7 @@ namespace CryptoExchange.Net
if (kvp.Value.GetType().IsArray)
{
var array = (Array)kvp.Value;
- foreach(var value in array)
+ foreach (var value in array)
formData.Add(kvp.Key, value.ToString());
}
else
@@ -363,7 +365,7 @@ namespace CryptoExchange.Net
var stringData = formData.ToString();
request.SetContent(stringData, contentType);
}
- }
+ }
///
/// Parse an error response from the server. Only used when server returns a status other than Success(200)