【问题标题】:UWP await on HttpClient not workingHttpClient上的UWP等待不起作用
【发布时间】:2018-07-23 22:16:39
【问题描述】:

我正在尝试从 Web API 获取 JSON 响应。

我能够使用类似代码在控制台应用程序中检索响应,但是在 UWP 中 await httpClient.GetAsync(uri); 无法按预期工作。

public static async Task<double> GetJson()
{
    using (var httpClient = new HttpClient())
    {
        Uri uri= new Uri("https://api.cryptonator.com/api/ticker/btc-usd");
        HttpResponseMessage response = await httpClient.GetAsync(uri);
        //Below code is not relevent since code is failing on await
        var result = response.Content.ReadAsStringAsync();
        var jsonResponse = Json.ToObjectAsync<ExchangeRate>(result);//
        jsonResponse.Wait();//
        ExchangeRate exchangeRateObj = jsonResponse.Result;//
        return 1.2;//
    }

}

背后的代码:

private void Button_Click(object sender,RoutedEventArgs e){

var ROC__ =  MyClass.GetJson();
ROC__.Wait();
currency_.ROC = ROC__.Result;

}

这里不起作用是什么意思?

它应该连接到 URL 并获取响应,并且应该为响应分配一些值。而是在使用 step into 或 Continue 进行调试时,控件退出当前行也会跳过后续行。 (我也已经在下一行进行了调试),应用程序只是冻结了。

我在 Stackoverflow 和其他博客上使用 HTTPClient 引用了类似的 JSON 解析代码,建议使用 System.Net.HttpWindows.Web.Http

相关问题: how-to-get-a-json-string-from-url

我认为任务正在运行并且它一直处于等待模式,这看起来很奇怪,因为调试模式不显示正在运行的代码,它只显示ready。也不例外。

我是做错了什么还是缺少一些 Nuget 参考资料?

请提出建议。

PS :httpClient.GetStringAsync 方法的情况相同。 在控制台应用程序上,这条线有效,但在 UWP 上无效

 var json = new WebClient().DownloadString("https://api.cryptonator.com/api/ticker/btc-usd");

httpclient-getasync-never-returns-on-xamarin-android不重复

  • 它不是 Xamarin,虽然它是基于 C# 的,但我的代码不同,它不是我关心的 WebApiClient 或 GetInformationAsync 方法。

【问题讨论】:

  • 为什么不同时等待ReadAsStringAsyncToObjectAsync
  • 控制台应用程序没有同步上下文。请在你的 UWP 应用中添加调用GetJson 的方法
  • 在 ReadAsStringAsync 上也尝试过等待,但不起作用。 GetJson() 方法已添加到 XAML 页面后面的代码中,
  • @CamiloTerevinto 他们seem to have 包含在 C# 7.1 中。
  • @Prateek 这是我内置到我的 UWP 库中的 HttpClient 的一个工作示例,并在一些不同的已发布和工作应用程序中具有 github.com/DotNetRussell/UWPLibrary/blob/master/BasecodeLibrary/…

标签: c# uwp dotnet-httpclient


【解决方案1】:

指定的代码有几个错误需要修复。首先,标记您的事件处理程序async

private async void Button_Click(object sender, RoutedEventArgs e)

其次,await GetJson 因为这是一个异步方法,所以最好在它的名字后面加上“Async”后缀;因此,等待GetJsonAsync

currency_.ROC = await MyClass.GetJsonAsync();

现在,回到 GetJsonAsync 本身,ReadAsStringAsyncToObjectAsync 也应该等待:

private static async Task<double> GetJsonAsync()
{
    using (var httpClient = new HttpClient())
    {
        Uri uri = new Uri("https://api.cryptonator.com/api/ticker/btc-usd");
        HttpResponseMessage response = await httpClient.GetAsync(uri);
        string result = await response.Content.ReadAsStringAsync();
        //Below code is not relevent since code is failing on await
        ExchangeRate exchangeRateObj = await Json.ToObjectAsync<ExchangeRate>(result);
        return 1.2;//
    }
}

之前不工作的原因是await调用和.Wait()的同步块之间的上下文切换导致了死锁。了解更多关于 here 的信息。

【讨论】:

  • 已针对问题上下文更改了方法名称。我曾尝试将 Button_Click 设为异步,之前出现错误..让我检查并更新..谢谢
  • 这个答案几乎是正确的。 OP的代码不起作用的原因是因为await调用和.Wait()的同步块之间的上下文切换导致了死锁,而不是因为GetAsync的结果是如何处理的
  • @CamiloTerevinto 你说得对;不错的收获!答案已更新。
  • 删除了Wait 并将Button_Click 设置为async 这两项更改都是必要的,response.StatusCode == System.Net.HttpStatusCode.OK 也是一种很好的做法。非常感谢 Aly 和 Camilo
  • @Prateek 你错了。您不必使处理程序异步即可工作。唯一能做的就是让它异步。正如我之前提到的,您唯一需要做的就是删除Wait() 函数调用。正如 Camilo 指出的那样,您遇到了僵局。
【解决方案2】:

您的代码运行良好。我在下面重新生成了它。摆脱你正在做的那个奇怪的等待电话。

这样做。创建一个新的 uwp 应用程序,粘贴下面的代码并在返回处放置一个断点,然后查看它是否被执行。

无论您是否使按钮处理程序异步,它都会起作用。如果您不将其设为 asnyc,则该请求将不会异步执行

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        SomeClass.GetJson();
    }

}

public class SomeClass
{
    public static async Task<double> GetJson()
    {
        using (var httpClient = new HttpClient())
        {
            Uri uri = new Uri("https://api.cryptonator.com/api/ticker/btc-usd");
            HttpResponseMessage response = await httpClient.GetAsync(uri);
            return 1.2;
        }
    }
}

我可以利用这一刻来无耻地插入我的 UWP 库。它为您完成这项工作。

https://github.com/DotNetRussell/UWPLibrary

BasecodeRequestManager.cs 文件中,有一个 BeginRequest 函数可以为您异步完成这项工作。该库还具有许多其他功能。

【讨论】:

  • 这不是真的“如果你不把它设为 asnyc,那么请求将不会被执行。”。将完成对SomeClass.GetJson() 的调用,并创建并启动一个非等待(即即发即弃)任务。
  • 你是对的,那是我打字时的失误。感谢您了解
  • 上面的代码和我的一样。 GetJson 在我这里是异步的。以静态方式调用它Button_Click 未声明为异步
  • @Prateek 我不确定你想说什么。但是,上面的代码已经过我的测试并且确实有效。
  • @AnthonyRussell 正如 Camilo 所说,如果我们不让它异步它就不会工作,Button_Click 必须是异步的。我认为您的库代码有效,但您发布的答案与我的代码没有太大区别。
【解决方案3】:

所以我自己尝试了这个。不幸的是,您的信息没有完成这么小的标题:
对于 Json-Handling,我使用了 Newtonsoft,因为我在 UWP 环境中没有找到 Json.ToObjectAsync
为了创建ExchangeRate- 类,我使用了Json2CSharp

以下是 ExchangeRate 类:

public class ExchangeRate
    {
        public string Error { get; set; }
        public bool Success { get; set; }
        public Ticker Ticker { get; set; }
        public int Timestamp { get; set; }
    }

public class Ticker
    {
        public string @Base { get; set; }
        public string Change { get; set; }
        public string Price { get; set; }
        public string Target { get; set; }
        public string Volume { get; set; }
    }

我将Button_Click-Method 更改为async void。通常不建议使用async void 而不是async Task。但是因为它是来自 UI 元素的处理程序,所以这不是问题,因为源无论如何都不会等待,而且您不应该直接从代码隐藏中调用此方法。
Button_Click-方法:

private async void Button_Click(object sender, RoutedEventArgs e)
        {
            var ROC__ = await MyClass.GetJson();
            //Do whatever you want with the result.
            //Its a double, maybe you want to return an ExchangeRate objcet insted
        }

GetJson-Method 内部,您需要为异步操作添加等待,或者直接在方法之后添加.Wait(),而不是在新行中。您需要这样做,因为当您调用异步操作并且 .Wait() 迟到时,任务会自动开始运行。所以你的 GetJson-Method 看起来像这样:

public static async Task<Double> GetJson()
        {

            using (var httpClient = new HttpClient())
            {
                Uri uri = new Uri("https://api.cryptonator.com/api/ticker/btc-usd");
                HttpResponseMessage response = await httpClient.GetAsync(uri);
                if (response.StatusCode == System.Net.HttpStatusCode.OK)
                {
                    var result = await response.Content.ReadAsStringAsync();
                    ExchangeRate rate = JsonConvert.DeserializeObject<ExchangeRate>(result); //Newtonsoft
                    return 1.2;
                }
                else
                {
                    return -1; //Example value
                }
            }
        }

此外,我添加了一个检查,如果请求成功,可以肯定的是,我们有响应。我之前说过:我认为您应该返回 ExchangeRate-object 而不是双精度对象,但这取决于您的上下文。

【讨论】:

  • 谢谢!我没有发布ExchangeRate 来使其成为minimal reproducible example,是的,只是让Button_Click 起作用
  • 没问题。但是因为不是很清楚/不明显,如果解析有问题,应该是在一个最小的例子中
猜你喜欢
  • 2019-10-10
  • 2018-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-20
  • 2018-12-15
相关资源
最近更新 更多