diff --git a/CryptoExchange.Net.UnitTests/RestClientTests.cs b/CryptoExchange.Net.UnitTests/RestClientTests.cs
index 1a93a3a..57f1f08 100644
--- a/CryptoExchange.Net.UnitTests/RestClientTests.cs
+++ b/CryptoExchange.Net.UnitTests/RestClientTests.cs
@@ -182,7 +182,7 @@ namespace CryptoExchange.Net.UnitTests
[TestCase("/sapi/test1", true)]
[TestCase("/sapi/test2", true)]
[TestCase("/api/test1", false)]
- [TestCase("sapi/test1", false)]
+ [TestCase("sapi/test1", true)]
[TestCase("/sapi/", true)]
public async Task PartialEndpointRateLimiterEndpoints(string endpoint, bool expectLimiting)
{
diff --git a/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs b/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs
index 09df970..1491072 100644
--- a/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs
+++ b/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs
@@ -82,6 +82,10 @@ namespace CryptoExchange.Net.UnitTests
{
}
+ public override void ProcessRequest(RestApiClient apiClient, RestRequestConfiguration requestConfig)
+ {
+ }
+
public string GetKey() => _credentials.Key;
public string GetSecret() => _credentials.Secret;
}
diff --git a/CryptoExchange.Net/Authentication/AuthenticationProvider.cs b/CryptoExchange.Net/Authentication/AuthenticationProvider.cs
index a092078..1c49f1d 100644
--- a/CryptoExchange.Net/Authentication/AuthenticationProvider.cs
+++ b/CryptoExchange.Net/Authentication/AuthenticationProvider.cs
@@ -51,30 +51,11 @@ namespace CryptoExchange.Net.Authentication
}
///
- /// Authenticate a request. Output parameters should include the providedParameters input
+ /// Authenticate a request
///
/// The Api client sending the request
- /// The uri for the request
- /// The method of the request
- /// If the requests should be authenticated
- /// Array serialization type
- /// The formatting of the request body
- /// Parameters that need to be in the Uri of the request. Should include the provided parameters if they should go in the uri
- /// Parameters that need to be in the body of the request. Should include the provided parameters if they should go in the body
- /// The headers that should be send with the request
- /// The position where the providedParameters should go
- public abstract void AuthenticateRequest(
- RestApiClient apiClient,
- Uri uri,
- HttpMethod method,
- ref IDictionary? uriParameters,
- ref IDictionary? bodyParameters,
- ref Dictionary? headers,
- bool auth,
- ArrayParametersSerialization arraySerialization,
- HttpMethodParameterPosition parameterPosition,
- RequestBodyFormat requestBodyFormat
- );
+ /// The request configuration
+ public abstract void ProcessRequest(RestApiClient apiClient, RestRequestConfiguration requestConfig);
///
/// SHA256 sign the data and return the bytes
diff --git a/CryptoExchange.Net/Clients/RestApiClient.cs b/CryptoExchange.Net/Clients/RestApiClient.cs
index c368e3d..a17ce6f 100644
--- a/CryptoExchange.Net/Clients/RestApiClient.cs
+++ b/CryptoExchange.Net/Clients/RestApiClient.cs
@@ -55,7 +55,7 @@ namespace CryptoExchange.Net.Clients
///
/// Request headers to be sent with each request
///
- protected Dictionary? StandardRequestHeaders { get; set; }
+ protected Dictionary StandardRequestHeaders { get; set; } = [];
///
/// Whether parameters need to be ordered
@@ -364,74 +364,58 @@ namespace CryptoExchange.Net.Clients
ParameterCollection? bodyParameters,
Dictionary? additionalHeaders)
{
- var uriParams = uriParameters == null ? null : CreateParameterDictionary(uriParameters);
- var bodyParams = bodyParameters == null ? null : CreateParameterDictionary(bodyParameters);
+ var requestConfiguration = new RestRequestConfiguration(
+ definition,
+ baseAddress,
+ uriParameters == null ? new Dictionary() : CreateParameterDictionary(uriParameters),
+ bodyParameters == null ? new Dictionary() : CreateParameterDictionary(bodyParameters),
+ new Dictionary(additionalHeaders ?? []),
+ definition.ArraySerialization ?? ArraySerialization,
+ definition.ParameterPosition ?? ParameterPositions[definition.Method],
+ definition.RequestBodyFormat ?? RequestBodyFormat);
- var uri = new Uri(baseAddress.AppendPath(definition.Path));
- var arraySerialization = definition.ArraySerialization ?? ArraySerialization;
- var bodyFormat = definition.RequestBodyFormat ?? RequestBodyFormat;
- var parameterPosition = definition.ParameterPosition ?? ParameterPositions[definition.Method];
-
- Dictionary? headers = null;
- if (AuthenticationProvider != null)
+ try
{
- try
- {
- AuthenticationProvider.AuthenticateRequest(
- this,
- uri,
- definition.Method,
- ref uriParams,
- ref bodyParams,
- ref headers,
- definition.Authenticated,
- arraySerialization,
- parameterPosition,
- bodyFormat
- );
- }
- catch (Exception ex)
- {
- throw new Exception("Failed to authenticate request, make sure your API credentials are correct", ex);
- }
+ AuthenticationProvider?.ProcessRequest(this, requestConfiguration);
}
+ catch (Exception ex)
+ {
+ throw new Exception("Failed to authenticate request, make sure your API credentials are correct", ex);
+ }
+
+ var queryString = requestConfiguration.GetQueryString(true);
+ if (!string.IsNullOrEmpty(queryString) && !queryString.StartsWith("?"))
+ queryString = $"?{queryString}";
- // Add the auth parameters to the uri, start with a new URI to be able to sort the parameters including the auth parameters
- if (uriParams != null)
- uri = uri.SetParameters(uriParams, arraySerialization);
-
+ var uri = new Uri(baseAddress.AppendPath(definition.Path) + queryString);
var request = RequestFactory.Create(definition.Method, uri, requestId);
request.Accept = Constants.JsonContentHeader;
- if (headers != null)
- {
- foreach (var header in headers)
- request.AddHeader(header.Key, header.Value);
- }
+ foreach (var header in requestConfiguration.Headers)
+ request.AddHeader(header.Key, header.Value);
- if (additionalHeaders != null)
+ foreach (var header in StandardRequestHeaders)
{
- foreach (var header in additionalHeaders)
+ // Only add it if it isn't overwritten
+ if (!requestConfiguration.Headers.ContainsKey(header.Key))
request.AddHeader(header.Key, header.Value);
- }
+ }
- if (StandardRequestHeaders != null)
+ if (requestConfiguration.ParameterPosition == HttpMethodParameterPosition.InBody)
{
- foreach (var header in StandardRequestHeaders)
+ var contentType = requestConfiguration.BodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
+ var bodyContent = requestConfiguration.GetBodyContent();
+ if (bodyContent != null)
{
- // Only add it if it isn't overwritten
- if (additionalHeaders?.ContainsKey(header.Key) != true)
- request.AddHeader(header.Key, header.Value);
+ request.SetContent(bodyContent, contentType);
}
- }
-
- if (parameterPosition == HttpMethodParameterPosition.InBody)
- {
- var contentType = bodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
- if (bodyParams != null && bodyParams.Count != 0)
- WriteParamBody(request, bodyParams, contentType);
else
- request.SetContent(RequestBodyEmptyContent, contentType);
+ {
+ if (requestConfiguration.BodyParameters != null && requestConfiguration.BodyParameters.Count != 0)
+ WriteParamBody(request, requestConfiguration.BodyParameters, contentType);
+ else
+ request.SetContent(RequestBodyEmptyContent, contentType);
+ }
}
return request;
diff --git a/CryptoExchange.Net/Objects/RequestDefinition.cs b/CryptoExchange.Net/Objects/RequestDefinition.cs
index 3d78592..6ce9ab4 100644
--- a/CryptoExchange.Net/Objects/RequestDefinition.cs
+++ b/CryptoExchange.Net/Objects/RequestDefinition.cs
@@ -76,6 +76,9 @@ namespace CryptoExchange.Net.Objects
{
Path = path;
Method = method;
+
+ if (!Path.StartsWith("/"))
+ Path = $"/{Path}";
}
///
diff --git a/CryptoExchange.Net/Objects/RestRequestConfiguration.cs b/CryptoExchange.Net/Objects/RestRequestConfiguration.cs
new file mode 100644
index 0000000..e748b9f
--- /dev/null
+++ b/CryptoExchange.Net/Objects/RestRequestConfiguration.cs
@@ -0,0 +1,124 @@
+using System.Collections.Generic;
+using System.Net.Http;
+
+namespace CryptoExchange.Net.Objects
+{
+ ///
+ /// Rest request configuration
+ ///
+ public class RestRequestConfiguration
+ {
+ private string? _bodyContent;
+ private string? _queryString;
+
+ ///
+ /// Http method
+ ///
+ public HttpMethod Method { get; set; }
+ ///
+ /// Whether the request needs authentication
+ ///
+ public bool Authenticated { get; set; }
+ ///
+ /// Base address for the request
+ ///
+ public string BaseAddress { get; set; }
+ ///
+ /// The request path
+ ///
+ public string Path { get; set; }
+ ///
+ /// Query parameters
+ ///
+ public IDictionary QueryParameters { get; set; }
+ ///
+ /// Body parameters
+ ///
+ public IDictionary BodyParameters { get; set; }
+ ///
+ /// Request headers
+ ///
+ public IDictionary Headers { get; set; }
+ ///
+ /// Array serialization type
+ ///
+ public ArrayParametersSerialization ArraySerialization { get; set; }
+ ///
+ /// Position of the parameters
+ ///
+ public HttpMethodParameterPosition ParameterPosition { get; set; }
+ ///
+ /// Body format
+ ///
+ public RequestBodyFormat BodyFormat { get; set; }
+
+ ///
+ /// ctor
+ ///
+ public RestRequestConfiguration(
+ RequestDefinition requestDefinition,
+ string baseAddress,
+ IDictionary queryParams,
+ IDictionary bodyParams,
+ IDictionary headers,
+ ArrayParametersSerialization arraySerialization,
+ HttpMethodParameterPosition parametersPosition,
+ RequestBodyFormat bodyFormat)
+ {
+ Method = requestDefinition.Method;
+ Authenticated = requestDefinition.Authenticated;
+ Path = requestDefinition.Path;
+ BaseAddress = baseAddress;
+ QueryParameters = queryParams;
+ BodyParameters = bodyParams;
+ Headers = headers;
+ ArraySerialization = arraySerialization;
+ ParameterPosition = parametersPosition;
+ BodyFormat = bodyFormat;
+ }
+
+ ///
+ /// Get the parameter collection based on the ParameterPosition
+ ///
+ public IDictionary GetPositionParameters()
+ {
+ if (ParameterPosition == HttpMethodParameterPosition.InBody)
+ return BodyParameters;
+
+ return QueryParameters;
+ }
+
+ ///
+ /// Get the query string. If it's not previously set it will return a newly formatted query string. If previously set return that.
+ ///
+ /// Whether to URL encode the parameter string if creating new
+ public string GetQueryString(bool urlEncode = true)
+ {
+ return _queryString ?? QueryParameters.CreateParamString(urlEncode, ArraySerialization);
+ }
+
+ ///
+ /// Set the query string of the request. Will be returned by subsequent calls
+ ///
+ public void SetQueryString(string value)
+ {
+ _queryString = value;
+ }
+
+ ///
+ /// Get the body content if it's previously set
+ ///
+ public string? GetBodyContent()
+ {
+ return _bodyContent;
+ }
+
+ ///
+ /// Set the body content for the request
+ ///
+ public void SetBodyContent(string content)
+ {
+ _bodyContent = content;
+ }
+ }
+}