【问题标题】:Call and get response from web api in a winform c# application在 winform c# 应用程序中调用并从 web api 获取响应
【发布时间】:2019-06-08 12:20:21
【问题描述】:

我正在 Windows 中制作一个简单的 WinForm 应用程序,我想获取一些有关外汇汇率的数据。所以我决定从 Oanda 调用一个 API。我尝试了几件事,但没有任何效果。它以 CSV 和 JSON 格式给出响应。我不知道哪个会更容易处理。

同样对于这种类型的响应,我无法创建它的模型类。 回应:

JSON:

{
  "meta": {
    "effective_params": {
      "data_set": "OANDA",
      "base_currencies": [
        "EUR"
      ],
      "quote_currencies": [
        "USD"
      ]
    },
    "endpoint": "spot",
    "request_time": "2019-06-08T12:05:23+00:00",
    "skipped_currency_pairs": []
  },
  "quotes": [
    {
      "base_currency": "EUR",
      "quote_currency": "USD",
      "bid": "1.13287",
      "ask": "1.13384",
      "midpoint": "1.13336"
    }
  ]
}

CSV:

base_currency,quote_currency,bid,ask,midpoint
EUR,USD,1.13287,1.13384,1.13336

我只需要这三个数字,哪种方法会有所帮助以及如何。

这段代码我已经试过了:

var client = new HttpClient();
client.BaseAddress = new Uri("https://www1.oanda.com/rates/api/v2/rates/");
HttpResponseMessage response = await client.GetAsync("spot.csv?api_key=<myapikey>&base=EUR&quote=USD");
string result = await response.Content.ReadAsStringAsync();
textBox1.Text = result;

编辑:我需要此调用的结果以进行进一步处理,因此我必须需要此方法来完成其执行,然后再继续进行

【问题讨论】:

    标签: c# .net winforms rest api


    【解决方案1】:

    首先从 Json 创建模型:

    1. 使用Json2C#之类的在线模型生成器,对于您发布的Json,生成的模型如下:

      public class EffectiveParams
      {
        public string data_set { get; set; }
        public List<string> base_currencies { get; set; }
        public List<string> quote_currencies { get; set; }
      }
      
      public class Meta
      {
        public EffectiveParams effective_params { get; set; }
        public string endpoint { get; set; }
        public DateTime request_time { get; set; }
        public List<object> skipped_currency_pairs { get; set; }
      }
      
      public class Quote
      {
        public string base_currency { get; set; }
        public string quote_currency { get; set; }
        public string bid { get; set; }
        public string ask { get; set; }
        public string midpoint { get; set; }
      }
      
      public class RootObject
      {
        public Meta meta { get; set; }
        public List<Quote> quotes { get; set; }
      }
      
    2. 1234563的客户,请使用以下简单的通用方法:

    假设它只是 GET 调用,只需将 Host 和 API 详细信息提供给下面的通用 Process 方法:

    public async Task<TResponse> Process<TResponse>(string host,string api)
    {
       // Execute Api call Async
       var httpResponseMessage = await MakeApiCall(host,api);
    
       // Process Json string result to fetch final deserialized model
       return await FetchResult<TResponse>(httpResponseMessage);
    } 
    
    public async Task<HttpResponseMessage> MakeApiCall(string host,string api)
    
    {   
        // Create HttpClient
        var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }) { BaseAddress = new Uri(host) };
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    
        // Make an API call and receive HttpResponseMessage
        HttpResponseMessage responseMessage = await client.GetAsync(api, HttpCompletionOption.ResponseContentRead);
    
        return responseMessage;
    }
    
    public async Task<T> FetchResult<T>(HttpResponseMessage result)
    {
        if (result.IsSuccessStatusCode)
        {
            // Convert the HttpResponseMessage to string
            var resultArray = await result.Content.ReadAsStringAsync();
    
            // Json.Net Deserialization
            var final = JsonConvert.DeserializeObject<T>(resultArray);
    
            return final;
        }
        return default(T);
    }
    

    使用方法:

    • 只需调用:

      var rootObj = await Process<RootObject>("https://www1.oanda.com/rates/", "api/v2/rates/");
      
    • 您收到反序列化的RootObject,如上图所示

    • 对于任何进一步复杂的处理,例如使用 http 正文向调用发送输入,以上通用代码需要进一步修改,目前仅针对您的要求

    编辑 1:(使条目调用同步)

    • 要使整体调用同步,请在最顶层使用GetAwaiter().GetResult(),Main 方法将被转换为,其余的将保持与示例中的相同(异步方法)

        void Main()
        {
           var rootObj =  Process<RootObject>("https://www1.oanda.com/rates/", "api/v2/rates/").GetAwaiter().GetResult();
        }
      

    编辑 2:(使完整的代码同步)

    void Main()
    {
        var rootObj = Process<RootObject>("https://www1.oanda.com/rates/", "api/v2/rates/");
    }
    
    
    public TResponse Process<TResponse>(string host, string api)
    {
        // Execute Api call
        var httpResponseMessage = MakeApiCall(host, api);
    
        // Process Json string result to fetch final deserialized model
        return FetchResult<TResponse>(httpResponseMessage);
    }
    
    public HttpResponseMessage MakeApiCall(string host, string api)
    
    {
        // Create HttpClient
        var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }) { BaseAddress = new Uri(host) };
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    
        // Make an API call and receive HttpResponseMessage
        HttpResponseMessage responseMessage = client.GetAsync(api, HttpCompletionOption.ResponseContentRead).GetAwaiter().GetResult();
    
        return responseMessage;
    }
    
    public T FetchResult<T>(HttpResponseMessage result)
    {
        if (result.IsSuccessStatusCode)
        {
            // Convert the HttpResponseMessage to string
            var resultArray = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
    
            // Json.Net Deserialization
            var final = JsonConvert.DeserializeObject<T>(resultArray);
    
            return final;
        }
        return default(T);
    }
    

    【讨论】:

    • 有几件事:Quote.bid/ask/midpoint 应该定义为decimalMeta.request_time 应该真正反序列化为 DateTimeOffset(在当前场景中没有偏移量 (+0),但作为一般规则......)。其余的都很好,IMO(因为现在您已将其转换为 .Net :)。有人可能会指出,由于您使用的是任务,HttpClient 应该是静态的。无论如何它都会工作。
    • 顺便说一句,在完整的框架中,System.Net.Mime.MediaTypeNames.Application 并没有——截至.Net 4.8——包括Json 作为 Mime 类型,但它在 .Net Core 中可用因为2.1.Net Standard 2.1?
    • 我收到了一个错误。 'await' 运算符只能在异步方法中使用。考虑使用“异步”修饰符标记此方法并将其返回类型更改为“任务”。我知道我们已经执行了异步方法,但我仍然收到此错误。 IDE:Visual Studio Community 2019 版。
    • @Inv3nt0r 这是一个真正的错误,这只是表明您没有将函数标记为async 或者您没有返回Task&lt;T&gt;,理想的情况是您编辑您的问题并提供细节。对于异步方法,您需要确保包括条目在内的完整链被标记为异步,您不应在任何地方使用Task.WaitTask.Result。另一方面,您甚至可以制作完整的非异步链,这也可以正常工作,但是对线程的阻塞调用
    • @Jimi 一组关于模型的有效点,它实际上是根据提供的 json 样本生成的,尽管我在理想数据类型方面同意你的看法。关于Task和静态HttpClient相关的点,我不确定我是否理解得很好。不包括 Json 作为默认的 Mime 类型确实令人困惑,我再次同意。感谢您的有效输入。
    【解决方案2】:

    您可以使用json2csharp 等在线服务获取您的json 模型,并使用Json.Net 对json 字符串进行序列化和反序列化。以下为您的 json 建模。

    public class EffectiveParams
    {
        public string data_set { get; set; }
        public List<string> base_currencies { get; set; }
        public List<string> quote_currencies { get; set; }
    }
    
    public class Meta
    {
        public EffectiveParams effective_params { get; set; }
        public string endpoint { get; set; }
        public DateTime request_time { get; set; }
        public List<object> skipped_currency_pairs { get; set; }
    }
    
    public class Quote
    {
        public string base_currency { get; set; }
        public string quote_currency { get; set; }
        public string bid { get; set; }
        public string ask { get; set; }
        public string midpoint { get; set; }
    }
    
    public class RootObject
    {
        public Meta meta { get; set; }
        public List<Quote> quotes { get; set; }
    }
    

    请注意,您可以将RootOject 更改为更具描述性的名称。

    因此,例如,要获得每个quotes出价、要价和中点值,您可以简单地执行以下操作:

    RootObject rootObj=JsonConvert.DeserializeObject(jsonString);
    
    //Get the required values.
    foreach(var quote in rootObj.quotes)
    {
    Console.WriteLine($"Bid : {quote.bid} Ask: {quote.ask} MidPoint: {quote.midpoint}");
    }
    

    【讨论】:

    • var quote = JsonConvert.DeserializeObject&lt;RootObject&gt;(jsonString).quotes.First();var quotes = JsonConvert.DeserializeObject&lt;RootObject&gt;(jsonString).quotes 然后foreach (var q in quotes) { ... }
    • @Jimi 我知道。这只是一个展示事情如何运作的例子。
    • 那么,让它成为一个工作示例并没有什么坏处。
    • @bolkay 非常感谢您的回答。效果非常好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-22
    • 1970-01-01
    • 2017-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-18
    相关资源
    最近更新 更多