mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-12 17:03:10 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b513e51b9 | |||
| d43b38a23a | |||
| 0987c0f9d1 | |||
| e2dde77023 | |||
| 104ac7caad | |||
| 8788dd3deb | |||
| f64cc5e9cf | |||
| 75d1bbc6e8 |
@@ -115,6 +115,7 @@ namespace CryptoExchange.Net.UnitTests
|
||||
TimeSpan.FromSeconds(1),
|
||||
null,
|
||||
"{}",
|
||||
1,
|
||||
"https://test.com/api",
|
||||
null,
|
||||
HttpMethod.Get,
|
||||
@@ -143,6 +144,7 @@ namespace CryptoExchange.Net.UnitTests
|
||||
TimeSpan.FromSeconds(1),
|
||||
null,
|
||||
"{}",
|
||||
1,
|
||||
"https://test.com/api",
|
||||
null,
|
||||
HttpMethod.Get,
|
||||
|
||||
@@ -83,6 +83,7 @@ namespace CryptoExchange.Net
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <param name="parameters">The parameters of the request</param>
|
||||
/// <param name="signed">Whether or not the request should be authenticated</param>
|
||||
/// <param name="requestBodyFormat">The format of the body content</param>
|
||||
/// <param name="parameterPosition">Where the parameters should be placed, overwrites the value set in the client</param>
|
||||
/// <param name="arraySerialization">How array parameters should be serialized, overwrites the value set in the client</param>
|
||||
/// <param name="requestWeight">Credits used for the request</param>
|
||||
@@ -97,6 +98,7 @@ namespace CryptoExchange.Net
|
||||
CancellationToken cancellationToken,
|
||||
Dictionary<string, object>? parameters = null,
|
||||
bool signed = false,
|
||||
RequestBodyFormat? requestBodyFormat = null,
|
||||
HttpMethodParameterPosition? parameterPosition = null,
|
||||
ArrayParametersSerialization? arraySerialization = null,
|
||||
int requestWeight = 1,
|
||||
@@ -108,11 +110,16 @@ namespace CryptoExchange.Net
|
||||
while (true)
|
||||
{
|
||||
currentTry++;
|
||||
var request = await PrepareRequestAsync(uri, method, cancellationToken, parameters, signed, parameterPosition, arraySerialization, requestWeight, deserializer, additionalHeaders, ignoreRatelimit).ConfigureAwait(false);
|
||||
var request = await PrepareRequestAsync(uri, method, cancellationToken, parameters, signed, requestBodyFormat, parameterPosition, arraySerialization, requestWeight, deserializer, additionalHeaders, ignoreRatelimit).ConfigureAwait(false);
|
||||
if (!request)
|
||||
return new WebCallResult(request.Error!);
|
||||
|
||||
var result = await GetResponseAsync<object>(request.Data, deserializer, cancellationToken, true).ConfigureAwait(false);
|
||||
if (!result)
|
||||
_logger.Log(LogLevel.Warning, $"[{result.RequestId}] Error received in {result.ResponseTime!.Value.TotalMilliseconds}ms: {result.Error}");
|
||||
else
|
||||
_logger.Log(LogLevel.Debug, $"[{result.RequestId}] Response received in {result.ResponseTime!.Value.TotalMilliseconds}ms{(OutputOriginalData ? (": " + result.OriginalData) : "")}");
|
||||
|
||||
if (await ShouldRetryRequestAsync(result, currentTry).ConfigureAwait(false))
|
||||
continue;
|
||||
|
||||
@@ -129,6 +136,7 @@ namespace CryptoExchange.Net
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <param name="parameters">The parameters of the request</param>
|
||||
/// <param name="signed">Whether or not the request should be authenticated</param>
|
||||
/// <param name="requestBodyFormat">The format of the body content</param>
|
||||
/// <param name="parameterPosition">Where the parameters should be placed, overwrites the value set in the client</param>
|
||||
/// <param name="arraySerialization">How array parameters should be serialized, overwrites the value set in the client</param>
|
||||
/// <param name="requestWeight">Credits used for the request</param>
|
||||
@@ -143,6 +151,7 @@ namespace CryptoExchange.Net
|
||||
CancellationToken cancellationToken,
|
||||
Dictionary<string, object>? parameters = null,
|
||||
bool signed = false,
|
||||
RequestBodyFormat? requestBodyFormat = null,
|
||||
HttpMethodParameterPosition? parameterPosition = null,
|
||||
ArrayParametersSerialization? arraySerialization = null,
|
||||
int requestWeight = 1,
|
||||
@@ -155,11 +164,16 @@ namespace CryptoExchange.Net
|
||||
while (true)
|
||||
{
|
||||
currentTry++;
|
||||
var request = await PrepareRequestAsync(uri, method, cancellationToken, parameters, signed, parameterPosition, arraySerialization, requestWeight, deserializer, additionalHeaders, ignoreRatelimit).ConfigureAwait(false);
|
||||
var request = await PrepareRequestAsync(uri, method, cancellationToken, parameters, signed, requestBodyFormat, parameterPosition, arraySerialization, requestWeight, deserializer, additionalHeaders, ignoreRatelimit).ConfigureAwait(false);
|
||||
if (!request)
|
||||
return new WebCallResult<T>(request.Error!);
|
||||
|
||||
var result = await GetResponseAsync<T>(request.Data, deserializer, cancellationToken, false).ConfigureAwait(false);
|
||||
if (!result)
|
||||
_logger.Log(LogLevel.Warning, $"[{result.RequestId}] Error received in {result.ResponseTime!.Value.TotalMilliseconds}ms: {result.Error}");
|
||||
else
|
||||
_logger.Log(LogLevel.Debug, $"[{result.RequestId}] Response received in {result.ResponseTime!.Value.TotalMilliseconds}ms{(OutputOriginalData ? (": " + result.OriginalData) : "")}");
|
||||
|
||||
if (await ShouldRetryRequestAsync(result, currentTry).ConfigureAwait(false))
|
||||
continue;
|
||||
|
||||
@@ -175,6 +189,7 @@ namespace CryptoExchange.Net
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <param name="parameters">The parameters of the request</param>
|
||||
/// <param name="signed">Whether or not the request should be authenticated</param>
|
||||
/// <param name="requestBodyFormat">The format of the body content</param>
|
||||
/// <param name="parameterPosition">Where the parameters should be placed, overwrites the value set in the client</param>
|
||||
/// <param name="arraySerialization">How array parameters should be serialized, overwrites the value set in the client</param>
|
||||
/// <param name="requestWeight">Credits used for the request</param>
|
||||
@@ -188,6 +203,7 @@ namespace CryptoExchange.Net
|
||||
CancellationToken cancellationToken,
|
||||
Dictionary<string, object>? parameters = null,
|
||||
bool signed = false,
|
||||
RequestBodyFormat? requestBodyFormat = null,
|
||||
HttpMethodParameterPosition? parameterPosition = null,
|
||||
ArrayParametersSerialization? arraySerialization = null,
|
||||
int requestWeight = 1,
|
||||
@@ -232,7 +248,7 @@ namespace CryptoExchange.Net
|
||||
|
||||
_logger.Log(LogLevel.Information, $"[{requestId}] Creating request for " + uri);
|
||||
var paramsPosition = parameterPosition ?? ParameterPositions[method];
|
||||
var request = ConstructRequest(uri, method, parameters?.OrderBy(p => p.Key).ToDictionary(p => p.Key, p => p.Value), signed, paramsPosition, arraySerialization ?? this.arraySerialization, requestId, additionalHeaders);
|
||||
var request = ConstructRequest(uri, method, parameters?.OrderBy(p => p.Key).ToDictionary(p => p.Key, p => p.Value), signed, paramsPosition, arraySerialization ?? this.arraySerialization, requestBodyFormat ?? this.requestBodyFormat, requestId, additionalHeaders);
|
||||
|
||||
string? paramString = "";
|
||||
if (paramsPosition == HttpMethodParameterPosition.InBody)
|
||||
@@ -261,9 +277,9 @@ namespace CryptoExchange.Net
|
||||
CancellationToken cancellationToken,
|
||||
bool expectedEmptyResponse)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
try
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
var response = await request.GetResponseAsync(cancellationToken).ConfigureAwait(false);
|
||||
sw.Stop();
|
||||
var statusCode = response.StatusCode;
|
||||
@@ -281,23 +297,22 @@ namespace CryptoExchange.Net
|
||||
responseLength ??= data.Length;
|
||||
responseStream.Close();
|
||||
response.Close();
|
||||
_logger.Log(LogLevel.Debug, $"[{request.RequestId}] Response received in {sw.ElapsedMilliseconds}ms{(OutputOriginalData ? (": " + data) : "")}");
|
||||
|
||||
if (!expectedEmptyResponse)
|
||||
{
|
||||
// Validate if it is valid json. Sometimes other data will be returned, 502 error html pages for example
|
||||
var parseResult = ValidateJson(data);
|
||||
if (!parseResult.Success)
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, parseResult.Error!);
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, parseResult.Error!);
|
||||
|
||||
// Let the library implementation see if it is an error response, and if so parse the error
|
||||
var error = await TryParseErrorAsync(parseResult.Data).ConfigureAwait(false);
|
||||
if (error != null)
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error!);
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error!);
|
||||
|
||||
// Not an error, so continue deserializing
|
||||
var deserializeResult = Deserialize<T>(parseResult.Data, deserializer, request.RequestId);
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), deserializeResult.Data, deserializeResult.Error);
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), deserializeResult.Data, deserializeResult.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -306,16 +321,16 @@ namespace CryptoExchange.Net
|
||||
var parseResult = ValidateJson(data);
|
||||
if (!parseResult.Success)
|
||||
// Not empty, and not json
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, parseResult.Error!);
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, parseResult.Error!);
|
||||
|
||||
var error = await TryParseErrorAsync(parseResult.Data).ConfigureAwait(false);
|
||||
if (error != null)
|
||||
// Error response
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error!);
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error!);
|
||||
}
|
||||
|
||||
// Empty success response; okay
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, default);
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, default);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -326,7 +341,7 @@ namespace CryptoExchange.Net
|
||||
responseStream.Close();
|
||||
response.Close();
|
||||
|
||||
return new WebCallResult<T>(statusCode, headers, sw.Elapsed, 0, null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, null);
|
||||
return new WebCallResult<T>(statusCode, headers, sw.Elapsed, 0, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, null);
|
||||
}
|
||||
|
||||
// Success status code, and we don't have to check for errors. Continue deserializing directly from the stream
|
||||
@@ -334,7 +349,7 @@ namespace CryptoExchange.Net
|
||||
responseStream.Close();
|
||||
response.Close();
|
||||
|
||||
return new WebCallResult<T>(statusCode, headers, sw.Elapsed, responseLength, OutputOriginalData ? desResult.OriginalData : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), desResult.Data, desResult.Error);
|
||||
return new WebCallResult<T>(statusCode, headers, sw.Elapsed, responseLength, OutputOriginalData ? desResult.OriginalData : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), desResult.Data, desResult.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -342,7 +357,6 @@ namespace CryptoExchange.Net
|
||||
// Http status code indicates error
|
||||
using var reader = new StreamReader(responseStream);
|
||||
var data = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||
_logger.Log(LogLevel.Warning, $"[{request.RequestId}] Error received in {sw.ElapsedMilliseconds}ms: {data}");
|
||||
responseStream.Close();
|
||||
response.Close();
|
||||
|
||||
@@ -354,29 +368,26 @@ namespace CryptoExchange.Net
|
||||
|
||||
if (error.Code == null || error.Code == 0)
|
||||
error.Code = (int)response.StatusCode;
|
||||
return new WebCallResult<T>(statusCode, headers, sw.Elapsed, data.Length, data, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error);
|
||||
return new WebCallResult<T>(statusCode, headers, sw.Elapsed, data.Length, data, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error);
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException requestException)
|
||||
{
|
||||
// Request exception, can't reach server for instance
|
||||
var exceptionInfo = requestException.ToLogString();
|
||||
_logger.Log(LogLevel.Warning, $"[{request.RequestId}] Request exception: " + exceptionInfo);
|
||||
return new WebCallResult<T>(null, null, null, null, null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new WebError(exceptionInfo));
|
||||
return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new WebError(exceptionInfo));
|
||||
}
|
||||
catch (OperationCanceledException canceledException)
|
||||
{
|
||||
if (cancellationToken != default && canceledException.CancellationToken == cancellationToken)
|
||||
{
|
||||
// Cancellation token canceled by caller
|
||||
_logger.Log(LogLevel.Warning, $"[{request.RequestId}] Request canceled by cancellation token");
|
||||
return new WebCallResult<T>(null, null, null, null, null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new CancellationRequestedError());
|
||||
return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new CancellationRequestedError());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Request timed out
|
||||
_logger.Log(LogLevel.Warning, $"[{request.RequestId}] Request timed out: " + canceledException.ToLogString());
|
||||
return new WebCallResult<T>(null, null, null, null, null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new WebError($"[{request.RequestId}] Request timed out"));
|
||||
return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new WebError($"Request timed out"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -412,6 +423,7 @@ namespace CryptoExchange.Net
|
||||
/// <param name="signed">Whether or not the request should be authenticated</param>
|
||||
/// <param name="parameterPosition">Where the parameters should be placed</param>
|
||||
/// <param name="arraySerialization">How array parameters should be serialized</param>
|
||||
/// <param name="bodyFormat">Format of the body content</param>
|
||||
/// <param name="requestId">Unique id of a request</param>
|
||||
/// <param name="additionalHeaders">Additional headers to send with the request</param>
|
||||
/// <returns></returns>
|
||||
@@ -422,6 +434,7 @@ namespace CryptoExchange.Net
|
||||
bool signed,
|
||||
HttpMethodParameterPosition parameterPosition,
|
||||
ArrayParametersSerialization arraySerialization,
|
||||
RequestBodyFormat bodyFormat,
|
||||
int requestId,
|
||||
Dictionary<string, string>? additionalHeaders)
|
||||
{
|
||||
@@ -502,7 +515,7 @@ namespace CryptoExchange.Net
|
||||
|
||||
if (parameterPosition == HttpMethodParameterPosition.InBody)
|
||||
{
|
||||
var contentType = requestBodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
|
||||
var contentType = bodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
|
||||
if (bodyParameters.Any())
|
||||
WriteParamBody(request, bodyParameters, contentType);
|
||||
else
|
||||
@@ -520,13 +533,13 @@ namespace CryptoExchange.Net
|
||||
/// <param name="contentType">The content type of the data</param>
|
||||
protected virtual void WriteParamBody(IRequest request, SortedDictionary<string, object> parameters, string contentType)
|
||||
{
|
||||
if (requestBodyFormat == RequestBodyFormat.Json)
|
||||
if (contentType == Constants.JsonContentHeader)
|
||||
{
|
||||
// Write the parameters as json in the body
|
||||
var stringData = JsonConvert.SerializeObject(parameters);
|
||||
request.SetContent(stringData, contentType);
|
||||
}
|
||||
else if (requestBodyFormat == RequestBodyFormat.FormData)
|
||||
else if (contentType == Constants.FormContentHeader)
|
||||
{
|
||||
// Write the parameters as form data in the body
|
||||
var stringData = parameters.ToFormData();
|
||||
@@ -580,14 +593,14 @@ namespace CryptoExchange.Net
|
||||
{
|
||||
var timeSyncParams = GetTimeSyncInfo();
|
||||
if (timeSyncParams == null)
|
||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, true, null);
|
||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, true, null);
|
||||
|
||||
if (await timeSyncParams.TimeSyncState.Semaphore.WaitAsync(0).ConfigureAwait(false))
|
||||
{
|
||||
if (!timeSyncParams.SyncTime || (DateTime.UtcNow - timeSyncParams.TimeSyncState.LastSyncTime < timeSyncParams.RecalculationInterval))
|
||||
{
|
||||
timeSyncParams.TimeSyncState.Semaphore.Release();
|
||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, true, null);
|
||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, true, null);
|
||||
}
|
||||
|
||||
var localTime = DateTime.UtcNow;
|
||||
@@ -616,7 +629,7 @@ namespace CryptoExchange.Net
|
||||
timeSyncParams.TimeSyncState.Semaphore.Release();
|
||||
}
|
||||
|
||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, true, null);
|
||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, true, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Kraken.Net.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converter for serializing decimal values as string
|
||||
/// </summary>
|
||||
public class DecimalStringWriterConverter : JsonConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanRead => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type objectType) => objectType == typeof(decimal) || objectType == typeof(decimal?);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) => writer.WriteValue(((decimal?)value)?.ToString(CultureInfo.InvariantCulture) ?? null);
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,16 @@
|
||||
<PackageId>CryptoExchange.Net</PackageId>
|
||||
<Authors>JKorf</Authors>
|
||||
<Description>A base package for implementing cryptocurrency API's</Description>
|
||||
<PackageVersion>6.2.0</PackageVersion>
|
||||
<AssemblyVersion>6.2.0</AssemblyVersion>
|
||||
<FileVersion>6.2.0</FileVersion>
|
||||
<PackageVersion>6.2.3</PackageVersion>
|
||||
<AssemblyVersion>6.2.3</AssemblyVersion>
|
||||
<FileVersion>6.2.3</FileVersion>
|
||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/JKorf/CryptoExchange.Net.git</RepositoryUrl>
|
||||
<PackageProjectUrl>https://github.com/JKorf/CryptoExchange.Net</PackageProjectUrl>
|
||||
<NeutralLanguage>en</NeutralLanguage>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageReleaseNotes>6.2.0 - Added SerializerOptions helper class for setting a default serializer, Added ParameterCollection helper class for easier parameter definition, Added extra helper methods AuthenticationProvider, Remove interface entries meant for internal use, Added support for writing int values to the EnumConverter</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>6.2.3 - Fixed requestBodyFormat parameter handling</PackageReleaseNotes>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using CryptoExchange.Net.Objects;
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace CryptoExchange.Net
|
||||
{
|
||||
@@ -8,6 +9,8 @@ namespace CryptoExchange.Net
|
||||
/// </summary>
|
||||
public static class ExchangeHelpers
|
||||
{
|
||||
private const string _allowedRandomChars = "ABCDEFGHIJKLMONOPQRSTUVWXYZabcdefghijklmonopqrstuvwxyz0123456789";
|
||||
|
||||
/// <summary>
|
||||
/// The last used id, use NextId() to get the next id and up this
|
||||
/// </summary>
|
||||
@@ -140,5 +143,43 @@ namespace CryptoExchange.Net
|
||||
return _lastId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a random string of specified length
|
||||
/// </summary>
|
||||
/// <param name="length">Length of the random string</param>
|
||||
/// <returns></returns>
|
||||
public static string RandomString(int length)
|
||||
{
|
||||
var randomChars = new char[length];
|
||||
|
||||
#if NETSTANDARD2_1_OR_GREATER
|
||||
for (int i = 0; i < length; i++)
|
||||
randomChars[i] = _allowedRandomChars[RandomNumberGenerator.GetInt32(0, _allowedRandomChars.Length)];
|
||||
#else
|
||||
var random = new Random();
|
||||
for (int i = 0; i < length; i++)
|
||||
randomChars[i] = _allowedRandomChars[random.Next(0, _allowedRandomChars.Length)];
|
||||
#endif
|
||||
|
||||
return new string(randomChars);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a random string of specified length
|
||||
/// </summary>
|
||||
/// <param name="source">The initial string</param>
|
||||
/// <param name="totalLength">Total length of the resulting string</param>
|
||||
/// <returns></returns>
|
||||
public static string AppendRandomString(string source, int totalLength)
|
||||
{
|
||||
if (totalLength < source.Length)
|
||||
throw new ArgumentException("Total length smaller than source string length", nameof(totalLength));
|
||||
|
||||
if (totalLength == source.Length)
|
||||
return source;
|
||||
|
||||
return source + RandomString(totalLength - source.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,6 +186,11 @@ namespace CryptoExchange.Net.Objects
|
||||
/// </summary>
|
||||
public IEnumerable<KeyValuePair<string, IEnumerable<string>>>? RequestHeaders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The request id
|
||||
/// </summary>
|
||||
public int? RequestId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The url which was requested
|
||||
/// </summary>
|
||||
@@ -217,6 +222,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <param name="code"></param>
|
||||
/// <param name="responseHeaders"></param>
|
||||
/// <param name="responseTime"></param>
|
||||
/// <param name="requestId"></param>
|
||||
/// <param name="requestUrl"></param>
|
||||
/// <param name="requestBody"></param>
|
||||
/// <param name="requestMethod"></param>
|
||||
@@ -226,6 +232,7 @@ namespace CryptoExchange.Net.Objects
|
||||
HttpStatusCode? code,
|
||||
IEnumerable<KeyValuePair<string, IEnumerable<string>>>? responseHeaders,
|
||||
TimeSpan? responseTime,
|
||||
int? requestId,
|
||||
string? requestUrl,
|
||||
string? requestBody,
|
||||
HttpMethod? requestMethod,
|
||||
@@ -235,6 +242,7 @@ namespace CryptoExchange.Net.Objects
|
||||
ResponseStatusCode = code;
|
||||
ResponseHeaders = responseHeaders;
|
||||
ResponseTime = responseTime;
|
||||
RequestId = requestId;
|
||||
|
||||
RequestUrl = requestUrl;
|
||||
RequestBody = requestBody;
|
||||
@@ -255,7 +263,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
public WebCallResult AsError(Error error)
|
||||
{
|
||||
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
|
||||
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -281,6 +289,11 @@ namespace CryptoExchange.Net.Objects
|
||||
/// </summary>
|
||||
public IEnumerable<KeyValuePair<string, IEnumerable<string>>>? RequestHeaders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The request id
|
||||
/// </summary>
|
||||
public int? RequestId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The url which was requested
|
||||
/// </summary>
|
||||
@@ -319,6 +332,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <param name="responseTime"></param>
|
||||
/// <param name="responseLength"></param>
|
||||
/// <param name="originalData"></param>
|
||||
/// <param name="requestId"></param>
|
||||
/// <param name="requestUrl"></param>
|
||||
/// <param name="requestBody"></param>
|
||||
/// <param name="requestMethod"></param>
|
||||
@@ -331,6 +345,7 @@ namespace CryptoExchange.Net.Objects
|
||||
TimeSpan? responseTime,
|
||||
long? responseLength,
|
||||
string? originalData,
|
||||
int? requestId,
|
||||
string? requestUrl,
|
||||
string? requestBody,
|
||||
HttpMethod? requestMethod,
|
||||
@@ -343,6 +358,7 @@ namespace CryptoExchange.Net.Objects
|
||||
ResponseTime = responseTime;
|
||||
ResponseLength = responseLength;
|
||||
|
||||
RequestId = requestId;
|
||||
RequestUrl = requestUrl;
|
||||
RequestBody = requestBody;
|
||||
RequestHeaders = requestHeaders;
|
||||
@@ -355,7 +371,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
public new WebCallResult AsDataless()
|
||||
{
|
||||
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestUrl, RequestBody, RequestMethod, RequestHeaders, Error);
|
||||
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, Error);
|
||||
}
|
||||
/// <summary>
|
||||
/// Copy as a dataless result
|
||||
@@ -363,14 +379,14 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
public new WebCallResult AsDatalessError(Error error)
|
||||
{
|
||||
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
|
||||
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new error result
|
||||
/// </summary>
|
||||
/// <param name="error">The error</param>
|
||||
public WebCallResult(Error? error) : this(null, null, null, null, null, null, null, null, null, default, error) { }
|
||||
public WebCallResult(Error? error) : this(null, null, null, null, null, null, null, null, null, null, default, error) { }
|
||||
|
||||
/// <summary>
|
||||
/// Copy the WebCallResult to a new data type
|
||||
@@ -380,7 +396,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
public new WebCallResult<K> As<K>([AllowNull] K data)
|
||||
{
|
||||
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestUrl, RequestBody, RequestMethod, RequestHeaders, data, Error);
|
||||
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, data, Error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -391,7 +407,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
public new WebCallResult<K> AsError<K>(Error error)
|
||||
{
|
||||
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestUrl, RequestBody, RequestMethod, RequestHeaders, default, error);
|
||||
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, default, error);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return Code != null ? $"{Code}: {Message} {Data}" : $"{Message} {Data}";
|
||||
return Code != null ? $"[{GetType().Name}] {Code}: {Message} {Data}" : $"[{GetType().Name}] {Message} {Data}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,16 +5,16 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Binance.Net" Version="9.1.4" />
|
||||
<PackageReference Include="Bitfinex.Net" Version="7.0.3" />
|
||||
<PackageReference Include="Bittrex.Net" Version="8.0.2" />
|
||||
<PackageReference Include="Bybit.Net" Version="3.1.3" />
|
||||
<PackageReference Include="CoinEx.Net" Version="6.0.2" />
|
||||
<PackageReference Include="Huobi.Net" Version="5.0.2" />
|
||||
<PackageReference Include="JK.Bitget.Net" Version="0.0.1" />
|
||||
<PackageReference Include="JK.OKX.Net" Version="1.4.1" />
|
||||
<PackageReference Include="KrakenExchange.Net" Version="4.1.4" />
|
||||
<PackageReference Include="Kucoin.Net" Version="5.0.4" />
|
||||
<PackageReference Include="Binance.Net" Version="9.1.5" />
|
||||
<PackageReference Include="Bitfinex.Net" Version="7.0.4" />
|
||||
<PackageReference Include="Bittrex.Net" Version="8.0.3" />
|
||||
<PackageReference Include="Bybit.Net" Version="3.2.1" />
|
||||
<PackageReference Include="CoinEx.Net" Version="6.0.3" />
|
||||
<PackageReference Include="Huobi.Net" Version="5.0.3" />
|
||||
<PackageReference Include="JK.Bitget.Net" Version="1.0.0" />
|
||||
<PackageReference Include="JK.OKX.Net" Version="1.4.2" />
|
||||
<PackageReference Include="KrakenExchange.Net" Version="4.1.5" />
|
||||
<PackageReference Include="Kucoin.Net" Version="5.0.5" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Binance.Net" Version="9.1.3" />
|
||||
<PackageReference Include="Bitfinex.Net" Version="7.0.2" />
|
||||
<PackageReference Include="Bittrex.Net" Version="8.0.1" />
|
||||
<PackageReference Include="Bybit.Net" Version="3.1.2" />
|
||||
<PackageReference Include="CoinEx.Net" Version="6.0.1" />
|
||||
<PackageReference Include="Huobi.Net" Version="5.0.1" />
|
||||
<PackageReference Include="JK.Bitget.Net" Version="0.0.1" />
|
||||
<PackageReference Include="JK.OKX.Net" Version="1.4.0" />
|
||||
<PackageReference Include="KrakenExchange.Net" Version="4.1.3" />
|
||||
<PackageReference Include="Kucoin.Net" Version="5.0.3" />
|
||||
<PackageReference Include="Binance.Net" Version="9.1.5" />
|
||||
<PackageReference Include="Bitfinex.Net" Version="7.0.4" />
|
||||
<PackageReference Include="Bittrex.Net" Version="8.0.3" />
|
||||
<PackageReference Include="Bybit.Net" Version="3.2.1" />
|
||||
<PackageReference Include="CoinEx.Net" Version="6.0.3" />
|
||||
<PackageReference Include="Huobi.Net" Version="5.0.3" />
|
||||
<PackageReference Include="JK.Bitget.Net" Version="1.0.0" />
|
||||
<PackageReference Include="JK.OKX.Net" Version="1.4.2" />
|
||||
<PackageReference Include="KrakenExchange.Net" Version="4.1.5" />
|
||||
<PackageReference Include="Kucoin.Net" Version="5.0.5" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -31,6 +31,18 @@ Make a one time donation in a crypto currency of your choice. If you prefer to d
|
||||
Alternatively, sponsor me on Github using [Github Sponsors](https://github.com/sponsors/JKorf).
|
||||
|
||||
## Release notes
|
||||
* Version 6.2.3 - 02 Dec 2023
|
||||
* Fixed requestBodyFormat parameter handling
|
||||
|
||||
* Version 6.2.2 - 02 Dec 2023
|
||||
* Added support for specifying the request body content type on a per request basis
|
||||
* Added DecimalStringWriterConverter
|
||||
* Added RequestId to WebCallResult model
|
||||
* Updated response logging
|
||||
|
||||
* Version 6.2.1 - 28 Oct 2023
|
||||
* Utility methods
|
||||
|
||||
* Version 6.2.0 - 24 Oct 2023
|
||||
* Added SerializerOptions helper class for setting a default serializer
|
||||
* Added ParameterCollection helper class for easier parameter definition
|
||||
|
||||
Reference in New Issue
Block a user