mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-09-03 06:01:40 +00:00
Refactored RestApiClient authentication to prevent duplicate query string / body serialization
This commit is contained in:
parent
3e365f83c9
commit
daf7ed9fe6
@ -182,7 +182,7 @@ namespace CryptoExchange.Net.UnitTests
|
|||||||
[TestCase("/sapi/test1", true)]
|
[TestCase("/sapi/test1", true)]
|
||||||
[TestCase("/sapi/test2", true)]
|
[TestCase("/sapi/test2", true)]
|
||||||
[TestCase("/api/test1", false)]
|
[TestCase("/api/test1", false)]
|
||||||
[TestCase("sapi/test1", false)]
|
[TestCase("sapi/test1", true)]
|
||||||
[TestCase("/sapi/", true)]
|
[TestCase("/sapi/", true)]
|
||||||
public async Task PartialEndpointRateLimiterEndpoints(string endpoint, bool expectLimiting)
|
public async Task PartialEndpointRateLimiterEndpoints(string endpoint, bool expectLimiting)
|
||||||
{
|
{
|
||||||
|
@ -82,6 +82,10 @@ namespace CryptoExchange.Net.UnitTests
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void ProcessRequest(RestApiClient apiClient, RestRequestConfiguration requestConfig)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public string GetKey() => _credentials.Key;
|
public string GetKey() => _credentials.Key;
|
||||||
public string GetSecret() => _credentials.Secret;
|
public string GetSecret() => _credentials.Secret;
|
||||||
}
|
}
|
||||||
|
@ -51,30 +51,11 @@ namespace CryptoExchange.Net.Authentication
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Authenticate a request. Output parameters should include the providedParameters input
|
/// Authenticate a request
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="apiClient">The Api client sending the request</param>
|
/// <param name="apiClient">The Api client sending the request</param>
|
||||||
/// <param name="uri">The uri for the request</param>
|
/// <param name="requestConfig">The request configuration</param>
|
||||||
/// <param name="method">The method of the request</param>
|
public abstract void ProcessRequest(RestApiClient apiClient, RestRequestConfiguration requestConfig);
|
||||||
/// <param name="auth">If the requests should be authenticated</param>
|
|
||||||
/// <param name="arraySerialization">Array serialization type</param>
|
|
||||||
/// <param name="requestBodyFormat">The formatting of the request body</param>
|
|
||||||
/// <param name="uriParameters">Parameters that need to be in the Uri of the request. Should include the provided parameters if they should go in the uri</param>
|
|
||||||
/// <param name="bodyParameters">Parameters that need to be in the body of the request. Should include the provided parameters if they should go in the body</param>
|
|
||||||
/// <param name="headers">The headers that should be send with the request</param>
|
|
||||||
/// <param name="parameterPosition">The position where the providedParameters should go</param>
|
|
||||||
public abstract void AuthenticateRequest(
|
|
||||||
RestApiClient apiClient,
|
|
||||||
Uri uri,
|
|
||||||
HttpMethod method,
|
|
||||||
ref IDictionary<string, object>? uriParameters,
|
|
||||||
ref IDictionary<string, object>? bodyParameters,
|
|
||||||
ref Dictionary<string, string>? headers,
|
|
||||||
bool auth,
|
|
||||||
ArrayParametersSerialization arraySerialization,
|
|
||||||
HttpMethodParameterPosition parameterPosition,
|
|
||||||
RequestBodyFormat requestBodyFormat
|
|
||||||
);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SHA256 sign the data and return the bytes
|
/// SHA256 sign the data and return the bytes
|
||||||
|
@ -55,7 +55,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Request headers to be sent with each request
|
/// Request headers to be sent with each request
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected Dictionary<string, string>? StandardRequestHeaders { get; set; }
|
protected Dictionary<string, string> StandardRequestHeaders { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether parameters need to be ordered
|
/// Whether parameters need to be ordered
|
||||||
@ -364,75 +364,59 @@ namespace CryptoExchange.Net.Clients
|
|||||||
ParameterCollection? bodyParameters,
|
ParameterCollection? bodyParameters,
|
||||||
Dictionary<string, string>? additionalHeaders)
|
Dictionary<string, string>? additionalHeaders)
|
||||||
{
|
{
|
||||||
var uriParams = uriParameters == null ? null : CreateParameterDictionary(uriParameters);
|
var requestConfiguration = new RestRequestConfiguration(
|
||||||
var bodyParams = bodyParameters == null ? null : CreateParameterDictionary(bodyParameters);
|
definition,
|
||||||
|
baseAddress,
|
||||||
|
uriParameters == null ? new Dictionary<string, object>() : CreateParameterDictionary(uriParameters),
|
||||||
|
bodyParameters == null ? new Dictionary<string, object>() : CreateParameterDictionary(bodyParameters),
|
||||||
|
new Dictionary<string, string>(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<string, string>? headers = null;
|
|
||||||
if (AuthenticationProvider != null)
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
AuthenticationProvider.AuthenticateRequest(
|
AuthenticationProvider?.ProcessRequest(this, requestConfiguration);
|
||||||
this,
|
|
||||||
uri,
|
|
||||||
definition.Method,
|
|
||||||
ref uriParams,
|
|
||||||
ref bodyParams,
|
|
||||||
ref headers,
|
|
||||||
definition.Authenticated,
|
|
||||||
arraySerialization,
|
|
||||||
parameterPosition,
|
|
||||||
bodyFormat
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw new Exception("Failed to authenticate request, make sure your API credentials are correct", ex);
|
throw new Exception("Failed to authenticate request, make sure your API credentials are correct", ex);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Add the auth parameters to the uri, start with a new URI to be able to sort the parameters including the auth parameters
|
var queryString = requestConfiguration.GetQueryString(true);
|
||||||
if (uriParams != null)
|
if (!string.IsNullOrEmpty(queryString) && !queryString.StartsWith("?"))
|
||||||
uri = uri.SetParameters(uriParams, arraySerialization);
|
queryString = $"?{queryString}";
|
||||||
|
|
||||||
|
var uri = new Uri(baseAddress.AppendPath(definition.Path) + queryString);
|
||||||
var request = RequestFactory.Create(definition.Method, uri, requestId);
|
var request = RequestFactory.Create(definition.Method, uri, requestId);
|
||||||
request.Accept = Constants.JsonContentHeader;
|
request.Accept = Constants.JsonContentHeader;
|
||||||
|
|
||||||
if (headers != null)
|
foreach (var header in requestConfiguration.Headers)
|
||||||
{
|
|
||||||
foreach (var header in headers)
|
|
||||||
request.AddHeader(header.Key, header.Value);
|
request.AddHeader(header.Key, header.Value);
|
||||||
}
|
|
||||||
|
|
||||||
if (additionalHeaders != null)
|
|
||||||
{
|
|
||||||
foreach (var header in additionalHeaders)
|
|
||||||
request.AddHeader(header.Key, header.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StandardRequestHeaders != null)
|
|
||||||
{
|
|
||||||
foreach (var header in StandardRequestHeaders)
|
foreach (var header in StandardRequestHeaders)
|
||||||
{
|
{
|
||||||
// Only add it if it isn't overwritten
|
// Only add it if it isn't overwritten
|
||||||
if (additionalHeaders?.ContainsKey(header.Key) != true)
|
if (!requestConfiguration.Headers.ContainsKey(header.Key))
|
||||||
request.AddHeader(header.Key, header.Value);
|
request.AddHeader(header.Key, header.Value);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (parameterPosition == HttpMethodParameterPosition.InBody)
|
if (requestConfiguration.ParameterPosition == HttpMethodParameterPosition.InBody)
|
||||||
{
|
{
|
||||||
var contentType = bodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
|
var contentType = requestConfiguration.BodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
|
||||||
if (bodyParams != null && bodyParams.Count != 0)
|
var bodyContent = requestConfiguration.GetBodyContent();
|
||||||
WriteParamBody(request, bodyParams, contentType);
|
if (bodyContent != null)
|
||||||
|
{
|
||||||
|
request.SetContent(bodyContent, contentType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (requestConfiguration.BodyParameters != null && requestConfiguration.BodyParameters.Count != 0)
|
||||||
|
WriteParamBody(request, requestConfiguration.BodyParameters, contentType);
|
||||||
else
|
else
|
||||||
request.SetContent(RequestBodyEmptyContent, contentType);
|
request.SetContent(RequestBodyEmptyContent, contentType);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,9 @@ namespace CryptoExchange.Net.Objects
|
|||||||
{
|
{
|
||||||
Path = path;
|
Path = path;
|
||||||
Method = method;
|
Method = method;
|
||||||
|
|
||||||
|
if (!Path.StartsWith("/"))
|
||||||
|
Path = $"/{Path}";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
124
CryptoExchange.Net/Objects/RestRequestConfiguration.cs
Normal file
124
CryptoExchange.Net/Objects/RestRequestConfiguration.cs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.Objects
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Rest request configuration
|
||||||
|
/// </summary>
|
||||||
|
public class RestRequestConfiguration
|
||||||
|
{
|
||||||
|
private string? _bodyContent;
|
||||||
|
private string? _queryString;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Http method
|
||||||
|
/// </summary>
|
||||||
|
public HttpMethod Method { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the request needs authentication
|
||||||
|
/// </summary>
|
||||||
|
public bool Authenticated { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Base address for the request
|
||||||
|
/// </summary>
|
||||||
|
public string BaseAddress { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The request path
|
||||||
|
/// </summary>
|
||||||
|
public string Path { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Query parameters
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<string, object> QueryParameters { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Body parameters
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<string, object> BodyParameters { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Request headers
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<string, string> Headers { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Array serialization type
|
||||||
|
/// </summary>
|
||||||
|
public ArrayParametersSerialization ArraySerialization { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Position of the parameters
|
||||||
|
/// </summary>
|
||||||
|
public HttpMethodParameterPosition ParameterPosition { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Body format
|
||||||
|
/// </summary>
|
||||||
|
public RequestBodyFormat BodyFormat { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
public RestRequestConfiguration(
|
||||||
|
RequestDefinition requestDefinition,
|
||||||
|
string baseAddress,
|
||||||
|
IDictionary<string, object> queryParams,
|
||||||
|
IDictionary<string, object> bodyParams,
|
||||||
|
IDictionary<string, string> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the parameter collection based on the ParameterPosition
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<string, object> GetPositionParameters()
|
||||||
|
{
|
||||||
|
if (ParameterPosition == HttpMethodParameterPosition.InBody)
|
||||||
|
return BodyParameters;
|
||||||
|
|
||||||
|
return QueryParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the query string. If it's not previously set it will return a newly formatted query string. If previously set return that.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="urlEncode">Whether to URL encode the parameter string if creating new</param>
|
||||||
|
public string GetQueryString(bool urlEncode = true)
|
||||||
|
{
|
||||||
|
return _queryString ?? QueryParameters.CreateParamString(urlEncode, ArraySerialization);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the query string of the request. Will be returned by subsequent <see cref="GetQueryString" /> calls
|
||||||
|
/// </summary>
|
||||||
|
public void SetQueryString(string value)
|
||||||
|
{
|
||||||
|
_queryString = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the body content if it's previously set
|
||||||
|
/// </summary>
|
||||||
|
public string? GetBodyContent()
|
||||||
|
{
|
||||||
|
return _bodyContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the body content for the request
|
||||||
|
/// </summary>
|
||||||
|
public void SetBodyContent(string content)
|
||||||
|
{
|
||||||
|
_bodyContent = content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user