勇哥注:
条件获取:可以避免相同数据的重复传输,进而提高性能。
条件更新:用于解决资源并发操作问题。
当我们对一个资源 进行修改或者删除的进修,通过条件更新的信息,我们可以知道在这之前该资源是否被其它人改动过。
条件更新的流程:
服务端:
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勇哥的分享

