如何使用 OpenAPI 规范验证 Bitstamp HTTP API v2 请求

问题描述 投票:0回答:1

我正在使用从 https://www.bitstamp.net/api/ 获得的 OpenAPI 规范 我使用了一个名为 NSWAG 的工具来为我生成客户端。

我似乎无法通过它进行身份验证,不断收到以下错误。我看了很多例子,但我似乎无法让它发挥作用。

响应的 HTTP 状态代码不是预期的 (403)。

状态:403
回应:
{“状态”:“错误”,“原因”:“无效签名”,“代码”:“API0005”}

BitstampClient.cs

public partial class BitstampClient
{
    private readonly string key;
    private readonly string secret;
    private readonly bool isPublicApi;
    
    public BitstampClient(string key, string secret, IHttpClientFactory httpFactory) : this(httpFactory.CreateClient($"BitstampClient-{key}"))
    {
        this.key = key;
        this.secret = secret;
        this.isPublicApi = false;
    }
    
    public BitstampClient(IHttpClientFactory httpFactory) : this(httpFactory.CreateClient($"BitstampClient-Public"))
    {
        this.isPublicApi = true;
    }
    
    public BitstampClient(string key, string secret) : this(new HttpClient())
    {
        this.key = key;
        this.secret = secret;
    }
    
    partial void PrepareRequest(HttpClient client, HttpRequestMessage request, StringBuilder urlBuilder)
    {
        if (!this.isPublicApi)
        {
            var xauth = $"{"BITSTAMP"} {this.key}";
            var nonce = Guid.NewGuid().ToString();  //DateTime.Now.Ticks;
            var time = DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond;
            var version = "v2";
            var contentType = "application/x-www-form-urlencoded";
            var signature = GetXAuthSignature(nonce, this.key, this.secret, "QWERTY");

            request.Headers.Add("X-Auth", xauth);
            request.Headers.Add("X-Auth-Signature", signature);
            request.Headers.Add("X-Auth-Nonce", nonce.ToString());
            request.Headers.Add("X-Auth-Timestamp", time.ToString());
            request.Headers.Add("X-Auth-Version", version);

            request.Content.Headers.ContentType = null;
        }
    }

    private string GetXAuthSignature(string nonce, string key, string secret, string clientId)
    {
        var msg = $"{nonce}{clientId}{key}";
        return ByteArrayToString(SignHMACSHA256(secret, StringToByteArray(msg))).ToUpper();
    }
    
    private static byte[] SignHMACSHA256(string key, byte[] data)
    {
        var hashMaker = new HMACSHA256(Encoding.ASCII.GetBytes(key));
        return hashMaker.ComputeHash(data);
    }

    private static byte[] StringToByteArray(string str)
    {
        return Encoding.ASCII.GetBytes(str);
    }
    
    private static string ByteArrayToString(byte[] hash)
    {
        return BitConverter.ToString(hash).Replace("-", "").ToLower();
    }
}
c# authentication httprequest
1个回答
0
投票

首先,我要感谢您在这个问题上给我一个良好的开端。通过您提供的示例,我能够开始使用

bitstamp API
..

我最终上的课是:

public partial class BitstampClient
{
    private readonly string publicKey;
    private readonly string secretKey;
    private readonly bool isPublicApi;

    public BitstampClient(string publicKey, string secretKey, HttpClient client) : this(client)
    {
        this.publicKey = publicKey;
        this.secretKey = secretKey;
        this.isPublicApi = false;
    }

    partial void PrepareRequest(HttpClient client, HttpRequestMessage request, string url)
    {
        if (!this.isPublicApi)
        {
            string apiKey = $"BITSTAMP {this.publicKey}";
            string timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();
            string nonce = Guid.NewGuid().ToString();
            string contentType = string.Empty;
            string version = "v2";
            string payloadString = string.Empty;

            if (request.Method == HttpMethod.Post)
            {
                request.Content.Headers.ContentType = null;

                var payload = request.Content?.ReadAsStringAsync().Result;
                if (!string.IsNullOrEmpty(payload))
                {
                    //contentType = "application/x-www-form-urlencoded";
                    contentType = "application/json";
                    payloadString = payload;
                }
            }

            url = url.Replace("https://", string.Empty).Replace("http://", string.Empty);
            var signature = $"{apiKey}{request.Method}{url}{contentType}{nonce}{timestamp}{version}{payloadString}";

            using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey)))
            {
                byte[] rawHmac = hmac.ComputeHash(Encoding.UTF8.GetBytes(signature));
                signature = BitConverter.ToString(rawHmac).Replace("-", "").ToLower();
            }

            request.Headers.Add("X-Auth", apiKey);
            request.Headers.Add("X-Auth-Signature", signature);
            request.Headers.Add("X-Auth-Nonce", nonce);
            request.Headers.Add("X-Auth-Timestamp", timestamp);
            request.Headers.Add("X-Auth-Version", version);
            //request.Headers.Add("Content-Type", contentType);

            //request.Content = null;
        }
    }

    public virtual Task<TradingPairsInfoResponse[]> GetTradingPairsInfoAsync2()
    {
        return GetTradingPairsInfoAsync2(System.Threading.CancellationToken.None);
    }

    [GenerateGenericOverride(nameof(GetTradingPairsInfoAsync))]
    public partial Task<TradingPairsInfoResponse[]> GetTradingPairsInfoAsync2(CancellationToken cancellationToken);  
}

调用

            string key = "";
            string secret = "";

            var httpClient = new HttpClient();
            var client = new BitstampClient(key, secret, httpClient);

            var accountBalance = await client.GetAccountBalancesAsync();

            var transactions = await client.GetOHLCDataAsync(Step._60, 100, null, null, null, market_symbol: "btcusd");

            //var pairs = await client.GetTradingPairsInfoAsync(); // not working
            var pairs = await client.GetTradingPairsInfoAsync2();

使用

OpenAPI
的原始
bitstamp
规范时请小心,因为它已损坏。例如,有一个部分:

"OrderTransaction": {
        "properties": {
          "tid": {
            "description": "Transaction ID.",
            "example": 1,
            "format": "int32",
            "readOnly": true,
            "type": "integer"
          },
          "price": {
            "description": "Price.",
            "example": "100.00",
            "readOnly": true,
            "type": "string"
          },
          "{from_currency}": {
            "description": "{from_currency} amount.",
            "example": "101.00",
            "readOnly": true,
            "type": "string"
          },

这会产生损坏的 C# 客户端代码,如下所示:

        /// <summary>
        /// {from_currency} amount.
        /// </summary>

        [System.Text.Json.Serialization.JsonPropertyName("{from_currency}")]

        [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)]
        public string {from_currency
    } { get; set; }

/// <summary>
/// {to_currency} amount.
/// </summary>

[System.Text.Json.Serialization.JsonPropertyName("{to_currency}")]

[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)]
public string
{ to_currency}
{ get; set; }

您在最初的帖子中没有提到这一点,所以我真的很想知道您是如何解决这个问题的。我最终在生成后手动更新了客户端代码。

另一个需要注意的地方 - 至少端点没有返回正确的数据类型。例如,端点

GetTradingPairsInfoAsync
返回类型为
TradingPairsInfoResponse
的对象,这是根据此处的规范:

    "/api/v2/trading-pairs-info/": {
      "get": {
        "description": "Return trading pairs info.",
        "operationId": "GetTradingPairsInfo",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TradingPairsInfoResponse"
                }
              }
            },
            "description": "Get operation"
          }
        },
        "security": [],
        "summary": "Trading pairs info",
        "tags": [
          "Market info"
        ]
      }
    },

但是,在调用

await client.GetTradingPairsInfoAsync();
时,您最终会遇到反序列化错误。正确的响应类型是对象数组
TradingPairsInfoResponse[]
..我不确定规范中是否还有其他损坏的端点..我通过创建覆盖通用类型的源生成器提出了一些修复。您可以在我的另一篇文章这里阅读有关此主题的更多信息。

请在评论中告诉我您对该规范的体验以及您如何解决上述问题:)

© www.soinside.com 2019 - 2024. All rights reserved.