一个具有缓存数据功能的HttpWebRequest工具类

背景:一个公共站点中的数据,供其它子站点共享,为了提高性能,简单实现了Http 1.1的缓存功能

特点:可以缓存Html数据到内存中;缓存具有过期时间;缓存过期后,通过再确认的方式来决定是否更新缓存;浏览器刷新后,无论缓存是否过期都会强制再验证;

未实现的包括:不能通过no-store来强制清空缓存,缓存再确认时没有验证Etag

/// <summary>
    /// 启用缓存的HttpRequest
    /// </summary>
    public static class CacheHttpRequest
    {
        private static Dictionary<String,HtmlLocalCache> _caches = new Dictionary<string,HtmlLocalCache>();
        private static object AddCacheLock = new Object();

        public static String GetHtml(String url)
        {
            HtmlLocalCache cache;
            //如果有缓存,且缓存尚未过期,或不需要重新验证,则直接返回内容
            if(_caches.TryGetValue(url,out cache))
            {
                if(!cache.NeedRevalidate())
                {
                    return cache.Html;
                }
            }

            var webRequest = (HttpWebRequest)WebRequest.Create(url);
            //如果有缓存,且需要重新验证,则设置它的IMS信息,为了简便,这里没有验证ETag
            if(cache != null)
            {
                webRequest.IfModifiedSince = cache.LastModified;
            }

            HttpWebResponse response = null;
            Stream stream = null;
            try
            {
                response = webRequest.GetHttpResponse();
                //如果服务器资源没有修改,则修改缓存信息后,返回内容
                if (cache != null && response.StatusCode == HttpStatusCode.NotModified)
                {
                    cache.UpdateCacheProperty(response);
                    return cache.Html;
                }

                //服务器资源已经修改,重新获取内容,并放入缓存
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    stream = response.GetResponseStream();
                    TextReader reader = new StreamReader(stream);

                    cache = new HtmlLocalCache();
                    cache.SetCacheProperty(response);
                    cache.Html = reader.ReadToEnd();
                    AddHtmlToCache(url, cache);

                    return cache.Html;
                }

                return "";
            }
            finally
            {
                if(response != null) response.Close();
                if(stream != null) stream.Dispose();
            }
        }

        private static void AddHtmlToCache(string url, HtmlLocalCache cache)
        {
            lock (AddCacheLock)
            {
                if (!_caches.ContainsKey(url))
                {
                    _caches.Add(url, cache);
                }
            }
        }

        /// <summary>
        /// 缓存数据
        /// </summary>
        private class HtmlLocalCache
        {
            public String Html;
            public DateTime LastModified;
            public DateTime? ExpiredTime;
            public DateTime HttpDate;
            private Object _updateLock = new Object();

            public Boolean NeedRevalidate()
            {
                if (ExpiredTime == null) return true;
                if (FromRefresh()) return true;
                return DateTime.Now > ExpiredTime;
            }

            /// <summary>
            /// 看是否是页面刷新
            /// </summary>
            /// <returns></returns>
            private static bool FromRefresh()
            {
                string requestCacheControl = HttpContext.Current.Request.Headers["Cache-Control"];
                String pragma = HttpContext.Current.Request.Headers["Pragma"];
                Boolean isRefresh = (pragma != null &&
                                     pragma.Equals("no-cache", StringComparison.OrdinalIgnoreCase)
                                     || requestCacheControl != null &&
                                     requestCacheControl.Equals("no-cache", StringComparison.OrdinalIgnoreCase));
                                    
                if (isRefresh) return true;
                return false;
            }

            private void SetCacheControl(string cacheControl)
            {
                if (String.IsNullOrEmpty(cacheControl)) return;

                if(cacheControl.Contains("max-age"))
                {
                    Double maxAge = Double.Parse(cacheControl.Substring(cacheControl.IndexOf('=')+1));
                    ExpiredTime = HttpDate.AddSeconds(maxAge);
                }
            }

            public void SetCacheProperty(HttpWebResponse response)
            {
                LastModified = response.LastModified;
                HttpDate = Convert.ToDateTime(response.Headers["Date"]);
                //使用cache-control来控制缓存过期时间,不使用expires
                SetCacheControl(response.Headers["Cache-Control"]);
            }

            public void UpdateCacheProperty(HttpWebResponse response)
            {
                lock (_updateLock)
                {
                    SetCacheProperty(response);
                }
            }
        }
    }
原文地址:https://www.cnblogs.com/hnsongbiao/p/7237850.html