【问题标题】:Why does an async method perform a HTTP request only once here?为什么异步方法在这里只执行一次 HTTP 请求?
【发布时间】:2013-12-25 19:45:18
【问题描述】:

简而言之,这是一个在 Visual Studio 2013 中使用 F5 运行时无法发出多个 HTTP 请求的 Windows 应用商店应用程序。目标平台是 Windows 8.1。

每次单击按钮时,都应该从公共服务器请求时间信息并显示响应。但是,正如 Fiddler 所示,实际的 HTTP 请求仅针对第一次单击发出,尽管相关的单击事件处理程序一遍又一遍地运行。

我已经通过API docs和其他几个地方,仍然没有。也许我忽略了某种配置问题,但我无法想象它会是什么。也许这里有人可以?

MainPage.xaml.cs

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.Web.Http;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace TestWindowsStore
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        private readonly Uri timeInfoHost = new Uri("http://jsontime-sharpnet.rhcloud.com/");
        private readonly HttpClient httpClient = new HttpClient();

        public MainPage()
        {
            this.InitializeComponent();

            var headers = httpClient.DefaultRequestHeaders;
            headers.UserAgent.ParseAdd("ie");
            headers.UserAgent.ParseAdd("Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)");
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            TimeInfo ti = await PerformTimeRequest();
            this.TimeText.Text = ti.datetime;
        }

        private async Task<TimeInfo> PerformTimeRequest()
        {
            string json = await httpClient.GetStringAsync(timeInfoHost);
            return Deserialize<TimeInfo>(json);
        }

        public T Deserialize<T>(string json)
        {
            var bytes = Encoding.Unicode.GetBytes(json);
            using (MemoryStream ms = new MemoryStream(bytes))
            {
                var jser = new DataContractJsonSerializer(typeof(T));
                return (T) jser.ReadObject(ms);
            }
        }
    }

    [DataContract]
    public class TimeInfo
    {
        [DataMember]
        public string tz;
        [DataMember]
        public string hour;
        [DataMember]
        public string datetime;
        [DataMember]
        public string second;
        [DataMember]
        public string error;
        [DataMember]
        public string minute;
    }
}

MainPage.xaml

<Page
x:Class="TestWindowsStore.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestWindowsStore"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <TextBox Name="TimeText" HorizontalAlignment="Left" Margin="491,242,0,0" TextWrapping="Wrap" VerticalAlignment="Top" IsReadOnly="True" Width="144"/>
    <Button Content="Get Time Async" HorizontalAlignment="Left" Margin="648,239,0,0" VerticalAlignment="Top" Click="Button_Click"/>

</Grid>
</Page>

为什么没有任何后续请求?我该如何解决?

【问题讨论】:

  • 您很可能在给定 URL 读取文件的缓存副本...
  • @ChrisK - 可能是在哪里缓存的副本?以及如何清空或禁用该缓存?
  • 我认为 HttpClient 将使用“全局”IE 缓存。您可以尝试在末尾附加一个随机 GUID 作为变量,以防止缓存吗?使网址看起来像“jsontime-sharpnet.rhcloud.com/?discard=GUID
  • 如果服务器正确设置了缓存标头,您永远不必担心这一点。为什么要通过网络两次检索相同的表示?当 max-age 到期时,下一个请求应该自动通过网络。如果您必须忽略服务器缓存指令,则发送 no-cache。
  • @DarrelMiller - 你不明白。这个问题的目的是找出为什么没有执行明确命令的请求。由于the docs for HttpClient 中没有提到缓存,我认为没有。如果我知道 HttpClient 根据 HTTP 标头缓存表示,这个问题就不存在了。

标签: c# wpf windows-8.1 windows-store dotnet-httpclient


【解决方案1】:

这里之所以只执行一次请求是因为默认情况下HttpClient会根据收到的Cache-Control headers(类似于HTTPClient Caching in .Net)使用缓存来存储响应。有两种方法可以解决它。

最简单的解决方案是使用HttpBaseProtocolFilter.CacheControl.ReadBehavior 属性来处理想要跳过缓存的情况:

var httpFilter = new Windows.Web.Http.Filters.HttpBaseProtocolFilter();
httpFilter.CacheControl.ReadBehavior = 
    Windows.Web.Http.Filters.HttpCacheReadBehavior.MostRecent;
var httpClient = new Windows.Web.Http.HttpClient(httpFilter);

但是,我认为更简洁的解决方案是修改您的服务器,使其包含适当的标头,告诉浏览器不要缓存响应,即:

Cache-Control: no-cache

例如:

HTTP/1.1 200 OK
Content-Length: 31
Content-Type: text/plain; charset=UTF-8
Cache-Control: no-cache

... 

第二种方案有两个优点:

  • 您不会再次使用的内容不会存储在客户端计算机中。
  • 其他请求可以从缓存中受益(使用HttpClient 的同一实例时)。

【讨论】:

  • 如果您无权访问服务器,您可以将 no-cache 选项作为请求标头发送。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-16
  • 1970-01-01
相关资源
最近更新 更多