勇哥注:
条件获取:可以避免相同数据的重复传输,进而提高性能。
条件更新:用于解决资源并发操作问题。
当我们对一个资源 进行修改或者删除的进修,通过条件更新的信息,我们可以知道在这之前该资源是否被其它人改动过。
条件更新的流程:
服务端:
2。返回body+(对等性判断值的)Etag报头
5。通过Etag报头与If-Match比较一致性,不一致就表示 被修改/删除的资源 已经被修改了,
这时回复一个412(Precondition Failed)的空消息
客户端:
1。发送请求
3。从回复中取得Etag报头
4。 修改:直接对资源修改,并提交修改后的资源
删除:向服务端发删除请求,为http请求加If-Match报头(内容就是3取得的Etab报头)
6。收第二次请求的结果,要么是412空消息,要么是有内容。
解决方案:
其中writeioData是个DTO,用来给post指令提供参数。
所以把它抽出来共享了。
服务端代码:
1。 post方法WriteBit的参数需要两个,一个是位号,一个是值,REST的post方法不允许有多个参数,因此使用一个calss WriteBitData来包装参数,并且使用json方式进行序列化传参。
2。 ComputeHashCode是用来取ioList变化后的HashCode(哈稀码)。
你不能直接用ioList.GetHashCode()来表达内容变化的列表,实际上这样取得的HashCode是完全一样的。
HashCode内部算法是跟对象的地址相关的,并不是跟对象的内容相关的。
ComputeHashCode函数就是取得随着列表内容变化的HashCode。
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; using WriteioData; 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) { //在执行WriteBit之前模拟已经被其它线程改了值了。 ioList[1] = 0; WebOperationContext.Current.IncomingRequest.CheckConditionalUpdate(ComputeHashCode(ioList)); WebOperationContext.Current.OutgoingResponse.SetETag(ComputeHashCode(ioList)); //因为上面会抛异常,实际上执行不到修改数据这里的 ioList[int.Parse(data.bitno)] = int.Parse(data.bitValue); } public static int ComputeHashCode<T>(List<T> list) where T : IEquatable<T> { unchecked // 允许溢出 { int hash = 17; foreach (var item in list) { hash = hash * 23 + EqualityComparer<T>.Default.GetHashCode(item); } return hash; } } } [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); } }
客户端代码:
GetAll是演示条件获取 的。
WriteIO是演示条件更新的。
using Newtonsoft.Json; 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; using WriteioData; namespace Client { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void BtnGetall_Click(object sender, EventArgs e) { eTag = ""; GetAll(); GetAll(); } private void WriteIO(int bitno,int data) { Uri uri2 = new Uri("http://127.0.0.1:9998/ReadWriteIO/all"); var req2 = HttpWebRequest.Create(uri2); req2.Method = "GET"; var res2 = req2.GetResponse(); Uri uri = new Uri("http://127.0.0.1:9998/ReadWriteIO/"); var res=HttpWebRequest.Create(uri); res.Method = "POST"; res.ContentType = "application/json"; try { var writeBitData = new WriteBitData { bitno = "0", bitValue = "0" }; string jsonData = JsonConvert.SerializeObject(writeBitData); byte[] buffer = Encoding.UTF8.GetBytes(jsonData); res.GetRequestStream().Write(Encoding.UTF8.GetBytes(jsonData), 0, buffer.Length); res.Headers.Add(HttpRequestHeader.IfMatch, res2.Headers[HttpResponseHeader.ETag]); RtbMsg.AppendText($"正准备写IO数据\r\n"); res.GetResponse(); } catch(WebException ex) { var response = ex.Response as HttpWebResponse; if (null == response) { throw; } if (response.StatusCode == HttpStatusCode.PreconditionFailed) { RtbMsg.AppendText("服务端数据已发生变化\r\n"); } else { throw; } } } 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; } } private void button1_Click(object sender, EventArgs e) { WriteIO(0, 0); } } }
本文原代码下载:
链接:https://pan.baidu.com/s/1uirGJS2Cy0dVGRfGm3x4Cg
提取码:0f7i
--来自百度网盘超级会员V6勇哥的分享

