勇哥注:
条件获取(Conditional Retrieval):
客户端访问一个资源会对它进行缓存,当再次访问这个资源的时候,如果资源相比缓存来说没任何变化,就不会返回真正的资源,而是回复一个“标识”,表明资源并没有改变。
这个特性是http天生带有的,因此像wsHttpBind, webHttpBind, basicHttpBind,REST服务等,都可以使用。
http的条件获取机制是这样的:
下面是两次读同一资源的过程,客户端与服务端按序号顺序进行。
客户端:
1。发送请求
3。从回复中取得Etag报头,缓存起来
4。为http请求加If-None-Match报头(内容就是第3步取得的Etab报头)
6。收第二次请求的结果,要么是304空内容,要么是有内容。
服务端:
2。返回body+Etag报头
5。通过If-None-Match确认当前要发送的资源是不是一致,如果一致发304(Not Modified)的空消息
如果不一致,则返回body+新的Etag报头
下面这个例子演示读写IO。
当读IO时发现并没有位改变过的时候,返回一个资源没有改变的标识,否则的话返回新的数据。
服务端代码:
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; namespace ConsoleClient { public class ReadWriteIO : IReadWriteIO { private static List<int> ioList = new List<int>() {1,1,1,1,1 }; public IEnumerable<int> GetAll() { int hashcode= ioList.GetHashCode(); //立刻检查eTag,如果相同则会返回304错误 WebOperationContext.Current.IncomingRequest.CheckConditionalRetrieve(hashcode); //到这里就是资源有变化了,返回etab+资源内容 WebOperationContext.Current.OutgoingResponse.SetETag(hashcode); return ioList; } public int GetBit(string bitno) { return ioList[int.Parse(bitno)]; } public void WriteBit(WriteBitData data) { ioList[int.Parse(data.bitno)] = int.Parse(data.bitValue); } } [ServiceContract(Namespace ="www.skcircle.com")] public interface IReadWriteIO { [WebGet(UriTemplate ="all")] IEnumerable<int> GetAll(); [WebInvoke(UriTemplate = "{bitno}", Method ="GET")] int GetBit(string bitno); [WebInvoke(UriTemplate = "/", Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)] void WriteBit(WriteBitData data); } public class WriteBitData { public string bitno { get; set; } public string bitValue { get; set; } } }
划重点:
private static List<int> ioList = new List<int>() {1,1,1,1,1 };
ioList必须是static静态的。否则的话GetAll中每次取得的哈希码都不同。
检查etag的代码,见代码中的注释。
客户端代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Windows.Forms; namespace Client { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void BtnGetall_Click(object sender, EventArgs e) { eTag = ""; GetAll(); GetAll(); } string eTag; private void GetAll() { Uri uri = new Uri("http://127.0.0.1:9998/ReadWriteIO/all"); var req=HttpWebRequest.Create(uri); if(!string.IsNullOrEmpty(eTag)) { req.Headers.Add(HttpRequestHeader.IfNoneMatch, eTag); } req.Method = "GET"; try { var res= req.GetResponse(); eTag = res.Headers[HttpResponseHeader.ETag]; using (StreamReader read = new StreamReader(res.GetResponseStream(), Encoding.UTF8)) { RtbMsg.AppendText($"{read.ReadToEnd()}\r\n"); } } catch(WebException ex) { var res = ex.Response as HttpWebResponse; if (res == null) throw; if(res.StatusCode== HttpStatusCode.NotModified) { RtbMsg.AppendText("IO状态没有发生变化"); return; } throw; } } } }
划重点:
因为是REST服务的调用,即然可以通过浏览器访问地址:http://127.0.0.1:9998/ReadWriteIO/all
那么当然也可以通过HttpWebRequest类得到一样的访问效果。
这种方式的好处是客户端不需要服务引用,整什么代理类,我只要知道一个域名就可以了。
客户端效果如下:
第二次取相同资源的时候,服务器抛出了异常。
表示资源没有变化。
本文源码下载:
链接:https://pan.baidu.com/s/12MscgB6MmppB8B5kOWgdeQ
提取码:tv0y
--来自百度网盘超级会员V6勇哥的分享

