簡體   English   中英

如何循環通過分頁 API 調用 C#?

[英]How to loop trough a paginated API Call in C#?

我正在使用 Web-API“Moralis-API”( https://docs.moralis.io/reference/getwalletnfts ),我從 NFT 錢包中獲取所有 NFT。 我的 API 呼叫如下所示:

using System.Net.Http.Headers;
var client = new HttpClient();
var request = new HttpRequestMessage
{
    Method = HttpMethod.Get,
    RequestUri = new Uri("https://deep-index.moralis.io/api/v2/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/nft?chain=eth&format=decimal&normalizeMetadata=false"),
    Headers =
    {
        { "accept", "application/json" },
        { "X-API-Key", "test" },
    },
};
using (var response = await client.SendAsync(request))
{
    response.EnsureSuccessStatusCode();
    var body = await response.Content.ReadAsStringAsync();
    Console.WriteLine(body);
}

API 返回一個 JSON 列表,其中包含具有唯一令牌 ID 的 NFT 和一個“光標”,它是分頁的頁面鍵。 它看起來像這樣:

{
  "total": 1600,
  "page": 1,
  "page_size": 100,
**  "cursor": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjdXN0b21QYXJhbXMiOnsid2FsbGV0QWRkcmVzcyI6IjB4ZDhkYTZiZjI2OTY0YWY5ZDdlZWQ5ZTAzZTUzNDE1ZDM3YWE5NjA0NSJ9LCJrZXlzIjpbIjE2NjY3NzIzMDkuNTQyIl0sIndoZXJlIjp7Im93bmVyX29mIjoiMHhkOGRhNmJmMjY5NjRhZjlkN2VlZDllMDNlNTM0MTVkMzdhYTk2MDQ1In0sImxpbWl0IjoxMDAsIm9mZnNldCI6MCwib3JkZXIiOltdLCJ0b3RhbCI6MTYwMCwicGFnZSI6MSwidGFpbE9mZnNldCI6NCwiaWF0IjoxNjY5MTI3OTUxfQ.UGpB3Qc88SJuU97dwVBwfYMkGcuH1-CaFbIKZ9iKots",
**  "result": [
    {
      "token_address": "0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85",
      "token_id": "103040680624633360426956226800459505851045291463662393946817594920946384752224",
      "amount": "1",
      "owner_of": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
      "token_hash": "bc95dd2a065742fab91ddd09b2c9a6fc",
      "block_number_minted": "16022477",
      "block_number": "16022477",
      "contract_type": "ERC721",
      "name": "Ethereum Name Service",
      "symbol": "ENS",
      "token_uri": null,
      "metadata": null,
      "last_token_uri_sync": null,
      "last_metadata_sync": "2022-11-22T02:13:59.639Z",
      "minter_address": null
    },

我的問題是,我不知道如何獲得所有 NFT。 API 每次通話最多只能給我 100 個 NFT。 但我想迭代到下一頁,以獲取所有 NFT。 我認為應該有一個帶循環的解決方案。 我還沒有找到任何關於堆棧溢出的解決方案。 也許有人可以在這里幫助我。 先感謝您。

根據文檔, cursor屬性用於將結果分頁到下一組。 看起來您需要反序列化響應 object 以同時獲取結果和 cursor 屬性。 我在這方面的工作比我應該做的要多一些,但這是針對您的問題的建議解決方案。

using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Text;

using Newtonsoft.Json;

namespace Program 
{
    public static class Program
    {
        public static async Task Main(string[] args)
        {
            var client = new HttpClient();
            var results = await GetAll(client);

            Console.WriteLine(JsonConvert.SerializeObject(results));
        }

        public static async Task<IList<NftWalletResponseItem>> GetAll(HttpClient client)
        {            
            var cursor = String.Empty;
            var results = new List<NftWalletResponseItem>();
            var builder = new UriBuilderExt("https", "deep-index.moralis.io")
                                .Path("/api/v2/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/nft")
                                .Query("chain", "eth")
                                .Query("format", "decimal")
                                .Query("normalizeMetadata", "false");

            var request = new HttpRequestMessage
            {
                Method = HttpMethod.Get,
                RequestUri = builder,
                Headers =
                {
                    { "Accept", "application/json" },
                    { "X-API-Key", "test" },
                }
            };

            for(var previous = -1; previous < results.Count; )
            {
                previous = results.Count;
                if(!String.IsNullOrWhiteSpace(cursor))
                {
                    builder.Query("cursor", cursor);
                }
                
                var response = await PerformRequest(client, request);
                results.AddRange(response.Result);
            }

            return results;
        }

        public static async Task<NftWalletResponse> PerformRequest(HttpClient client, HttpRequestMessage message)
        {
            var _default = new NftWalletResponse();

            using (var response = await client.SendAsync(message))
            {
                if(response.IsSuccessStatusCode)
                {
                    var body = await response.Content.ReadAsStringAsync();
                    NftWalletResponse result = JsonConvert.DeserializeObject<NftWalletResponse>(body) ?? _default;
                    return result;
                }
            }

            return _default;
        }
    }

    public class NftWalletResponse 
    {
        [JsonProperty("total")]
        public int Total { get; set; }
        [JsonProperty("page")]
        public int Page { get; set; }
        [JsonProperty("page_size")]
        public int PageSize { get; set; }
        [JsonProperty("cursor")]
        public string Cursor { get; set; }
        [JsonProperty("result")]
        public IList<NftWalletResponseItem> Result { get; set; }

        public NftWalletResponse()
        {
            Cursor = String.Empty;
            Result = new List<NftWalletResponseItem>();
        }
    }

    public class NftWalletResponseItem
    {
        [JsonProperty("amount")]
        public int Amount { get; set; }
        [JsonProperty("block_number")]
        public string BlockNumber { get; set; }
        [JsonProperty("block_number_minted")]
        public string BlockNumberMinted { get; set; }
        [JsonProperty("contract_type")]
        public string ContractType { get; set; }
        [JsonProperty("last_metadata_sync")]
        public DateTime LastMetadataSync { get; set; }
        // [JsonProperty("last_token_uri_sync")]
        // public <define type> LastTokenUriSync { get; set; }
        // [JsonProperty("metadata")]
        // public <define type> Metadata { get; set; }
        // [JsonProperty("minter_address)]
        // public <define type> MinterAddress { get; set; }
        [JsonProperty("name")]
        public string Name { get; set; }
        [JsonProperty("owner_of")]
        public string OwnerOf { get; set; }
        [JsonProperty("symbol")]
        public string Symbol { get; set; }
        [JsonProperty("token_address")]
        public string TokenAddress { get; set; }
        [JsonProperty("token_hash")]
        public string TokenHash { get; set; }
        [JsonProperty("token_id")]
        public string TokenId { get; set; }
        [JsonProperty("token_uri")]
        public string TokenUri { get; set; }

        public NftWalletResponseItem()
        {
            BlockNumber = String.Empty;
            BlockNumberMinted = String.Empty;
            ContractType = String.Empty;
            LastMetadataSync = DateTime.UtcNow;
            Name = String.Empty;
            OwnerOf = String.Empty;
            Symbol = String.Empty;
            TokenAddress = String.Empty;
            TokenHash = String.Empty;
            TokenId = String.Empty;
            TokenUri = String.Empty;
        }
    }

    public static class ArrayExt {
        public static void Deconstruct<T>(this T[] srcArray, out T a0) {
            if (srcArray == null || srcArray.Length < 1)
                throw new ArgumentException(nameof(srcArray));

            a0 = srcArray[0];
        }

        public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1) {
            if (srcArray == null || srcArray.Length < 2)
                throw new ArgumentException(nameof(srcArray));

            a0 = srcArray[0];
            a1 = srcArray[1];
        }

        public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2) {
            if (srcArray == null || srcArray.Length < 3)
                throw new ArgumentException(nameof(srcArray));

            a0 = srcArray[0];
            a1 = srcArray[1];
            a2 = srcArray[2];
        }

        public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3) {
            if (srcArray == null || srcArray.Length < 4)
                throw new ArgumentException(nameof(srcArray));

            a0 = srcArray[0];
            a1 = srcArray[1];
            a2 = srcArray[2];
            a3 = srcArray[3];
        }

        public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3, out T a4) {
            if (srcArray == null || srcArray.Length < 5)
                throw new ArgumentException(nameof(srcArray));

            a0 = srcArray[0];
            a1 = srcArray[1];
            a2 = srcArray[2];
            a3 = srcArray[3];
            a4 = srcArray[4];
        }

        public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3, out T a4, out T a5) {
            if (srcArray == null || srcArray.Length < 6)
                throw new ArgumentException(nameof(srcArray));

            a0 = srcArray[0];
            a1 = srcArray[1];
            a2 = srcArray[2];
            a3 = srcArray[3];
            a4 = srcArray[4];
            a5 = srcArray[5];
        }

        public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3, out T a4, out T a5, out T a6) {
            if (srcArray == null || srcArray.Length < 7)
                throw new ArgumentException(nameof(srcArray));

            a0 = srcArray[0];
            a1 = srcArray[1];
            a2 = srcArray[2];
            a3 = srcArray[3];
            a4 = srcArray[4];
            a5 = srcArray[5];
            a6 = srcArray[6];
        }

        public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3, out T a4, out T a5, out T a6, out T a7) {
            if (srcArray == null || srcArray.Length < 8)
                throw new ArgumentException(nameof(srcArray));

            a0 = srcArray[0];
            a1 = srcArray[1];
            a2 = srcArray[2];
            a3 = srcArray[3];
            a4 = srcArray[4];
            a5 = srcArray[5];
            a6 = srcArray[6];
            a7 = srcArray[7];
        }
    }

    public class UriBuilderExt
    {
        private UriBuilder _builder;

        public UriBuilderExt()
        {
            _builder = new UriBuilder();
        }

        public UriBuilderExt(string uri)
        {
            _builder = new UriBuilder(uri);
        }

        public UriBuilderExt(string schemeName, string hostName)
        {
            _builder = new UriBuilder(schemeName, hostName);
        }

        public UriBuilderExt(string schemeName, string hostName, int port)
        {
            _builder = new UriBuilder(schemeName, hostName, port);
        }

        public UriBuilderExt(string schemeName, string hostName, int port, string path)
        {
            _builder = new UriBuilder(schemeName, hostName, port, path);
        }

        public UriBuilderExt(string schemeName, string hostName, int port, string path, string fragment)
        {
            _builder = new UriBuilder(schemeName, hostName, port, path, fragment);
        }
        
        public UriBuilderExt(Uri uri)
        {
            _builder = new UriBuilder(uri);
        }

        public static implicit operator UriBuilderExt(string uri)
        {
            return new UriBuilderExt(uri);
        }

        public static implicit operator UriBuilderExt(Uri uri)
        {
            return new UriBuilderExt(uri);
        }

        public static implicit operator Uri(UriBuilderExt builder)
        {
            return builder.Build();
        }

        public Uri Build()
        {
            return _builder.Uri;
        }

        public UriBuilderExt Fragment(string fragment)
        {
            _builder.Fragment = fragment;
            return this;
        }

        public UriBuilderExt Host(string host)
        {
            _builder.Host = host;
            return this;
        }

        public UriBuilderExt Password(string password)
        {
            _builder.Password = password;
            return this;
        }

        public UriBuilderExt Path(string path)
        {
            _builder.Path = path;
            return this;
        }

        public UriBuilderExt Port(int port)
        {
            _builder.Port = port;
            return this;
        }

        public UriBuilderExt Query(string key, string value)
        {
            var qs = new Dictionary<string, string?>();
            var builder = new StringBuilder();
            
            foreach(var entry in _builder.Query.Split('&'))
            {
                var (k, v) = entry.Split('=');
                qs.Add(k, v);
            }

            qs.Add(key, value);
            
            foreach(var entry in qs)
            {
                if(builder.Length > 0)
                {
                    builder.Append('&');
                }

                if(!String.IsNullOrEmpty(entry.Value))
                {
                    builder.AppendJoin('=', entry.Key, entry.Value);
                }
                else
                {
                    builder.Append(entry.Key);
                }
            }

            _builder.Query = builder.ToString();
            return this;
        }

        public UriBuilderExt Scheme(string scheme)
        {
            _builder.Scheme = scheme;
            return this;
        }

        public UriBuilderExt UserName(string user)
        {
            _builder.UserName = user;
            return this;
        }
    }
}

原諒一些過度工程,但我已經在 JavaScript 工作了一段時間,我開始期待這方面的一些功能,例如從序列類型中解構賦值,以及更少的手動實現來構建URL(Node 有一個很棒的 API 對應URL.prototype.searchParams )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM