勇哥注:
这里补充几个WCF REST的知识点。
rest到底要不要元数据?
rest的帮助页怎么弄?
rest怎么改成支持json消息?
rest支持可靠的消息传递吗?
rest服务怎么用webClient来访问?(其实就是模拟浏览器http的访问过程)
(一)元数据
wcf的rest服务不需要你指定元数据。
在WCF中,特别是当您创建REST服务时,关于元数据的处理与传统SOAP服务有所不同。
WCF REST服务通常不依赖于SOAP的WSDL(Web Services Description Language)元数据,
因为REST服务的设计哲学是基于HTTP的资源表示和状态转移,而不是像SOAP那样通过WSDL来描述服务接口。
<serviceMetadata> 元素在WCF中主要用于SOAP服务,
它允许客户端通过HTTP GET请求来检索WSDL文件,从而自动生成代理类或其他客户端代码。
然而,在REST服务中,通常不需要WSDL,因为REST服务通常通过HTTP方法(如GET、POST、PUT、DELETE)
和媒体类型(如application/json、application/xml)来定义其接口。
因此,当您尝试在WCF REST服务的配置中设置<serviceMetadata>时,它实际上不会对REST服务的客户端发现或调用产生直接影响。
客户端不需要WSDL来调用REST服务,而是需要知道资源的URI、HTTP方法和请求/响应的格式。
所以下面这样的定义是没有意义的。
<serviceBehaviors> <behavior name="NewBehavior0"> <serviceMetadata httpGetUrl="http://192.168.216.129:3721/meta" httpsGetEnabled="true" /> </behavior> </serviceBehaviors>
(二)帮助页
就是那句 <webHttp helpEnabled="true" />
此时访问地址就是:http://192.168.216.129:3721/employees/help
即:{终结点地址}/Help
<system.serviceModel> <behaviors> <endpointBehaviors> <behavior name="NewBehavior1"> <webHttp helpEnabled="true" /> </behavior> </endpointBehaviors> </behaviors> <services> <service name="Service.EmployeesService"> <endpoint address="http://192.168.216.129:3721/employees" behaviorConfiguration="NewBehavior1" binding="webHttpBinding" contract="Contract.IEmployees" /> </service> </services> </system.serviceModel>
默认的帮助页是下面这样的:
我们把契约的方法加上描述特性Description
public interface IEmployees { [WebGet(UriTemplate = "all")] [Description("获取所有员工列表")] IEnumerable<Employee> GetAll(); [WebGet(UriTemplate = "{id}")] [Description("获取指定ID的员工, 参数:(string id)")] Employee Get(string id); [WebInvoke(UriTemplate = "/", Method = "POST")] [Description("创建一个新的员工, 参数:(Employee employee)")] void Create(Employee employee); [WebInvoke(UriTemplate = "/", Method = "PUT")] [Description("修改现有员工信息, 参数:(Employee employee)")] void Update(Employee employee); [WebInvoke(UriTemplate = "{id}", Method = "DELETE")] [Description("删除指定ID的员工, 参数:(string id)")] void Delete(string id); }
现在的帮助页就是这样的:
(三)json
REST服务具有两种基本的消息格式(Xml和Json),默认会使用Xml消息格式。
下面我们改为Json格式:
首先把契约的方法的RequestFormat和ResponseFormat都改为json。
[WebGet(UriTemplate = "all",RequestFormat = WebMessageFormat.Json,ResponseFormat = WebMessageFormat.Json)] [Description("获取所有员工列表")] IEnumerable<Employee> GetAll();
这时候调用者直接收到的就是json格式消息。
还可以设置为系统自动选择,这个是有一套自动选择的规则的(主要是看调用者怎么选择)。
详细可以看:https://www.cnblogs.com/artech/archive/2012/02/07/wcf-rest-help-page.html
(四)rest支持可靠的消息传递吗?
WCF与REST
首先,WCF支持多种通信协议和消息格式,包括SOAP(Simple Object Access Protocol)和REST。
SOAP通常与复杂的、基于XML的消息传递相关联,
而REST则倾向于使用简单的HTTP方法和资源表示(如JSON或XML)来进行通信。
REST与可靠消息
REST本身并不直接提供“可靠消息”的保证。REST的设计哲学是倾向于简单性和无状态性,
这意味着每个请求都应该独立于其他请求,
服务器不应该保持客户端的会话状态。因此,REST通常不处理消息传递的可靠性、事务性或持久性等问题,
这些问题更多地是由应用层或中间件来解决的。
WCF中的可靠消息传递
然而,WCF提供了对可靠消息传递的支持,但这主要是通过SOAP协议和WS-ReliableMessaging(WS-RM)规范来实现的。
WS-RM为SOAP消息提供了一种可靠的、有序的、基于会话的传递机制,以确保消息在不可靠的网络环境中能够可靠地传递。
WCF REST服务的可靠消息传递
对于WCF REST服务,虽然REST本身不直接支持可靠消息传递,但你可以通过以下几种方式来实现或增强消息的可靠性:
应用层重试机制:
在客户端实现重试逻辑,当请求失败时自动重试。
这可以通过简单的重试策略(如固定间隔重试)或更复杂的策略(如指数退避重试)来实现。
持久化请求和响应:
将请求和响应存储在数据库中或其他持久化存储中,以便在出现网络故障或服务器故障时能够恢复它们。
使用中间件:
在WCF REST服务之前或之后部署中间件,该中间件可以提供消息队列、事务处理、消息重试等可靠性功能。
自定义协议或扩展:
虽然这通常不是首选方法,但在某些情况下,你可能需要自定义协议或扩展WCF REST服务以支持特定的可靠性要求。
结论
综上所述,WCF REST服务本身并不直接支持可靠消息传递,但你可以通过应用层重试机制、持久化请求和响应、
使用中间件或自定义协议等方法来增强消息的可靠性。这些方法可以根据你的具体需求和应用程序的复杂性来选择。
(五)演示代码
代码:
演示了使用webClient类进行Post, Put, Get, Delete的REST访问。
using Contract; 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; namespace webClient { public partial class Form1 : Form { private WebClient webClient = new WebClient(); private string serverIP = "http://192.168.216.129:3721/employees"; public Form1() { InitializeComponent(); } private string Get(string contentType, string accept,string cmdURL) { if (!string.IsNullOrEmpty(contentType)) { webClient.Headers.Add("Content-Type", contentType); } if (!string.IsNullOrEmpty(accept)) { webClient.Headers.Add("Accept", accept); } using (StreamReader reader = new StreamReader(webClient.OpenRead(cmdURL))) { return reader.ReadToEnd(); } } private string Post(string contentType, string accept, string cmdURL,string jsonStr) { if (!string.IsNullOrEmpty(contentType)) { webClient.Headers.Add("Content-Type", contentType); } if (!string.IsNullOrEmpty(accept)) { webClient.Headers.Add("Accept", accept); } byte[] data = Encoding.UTF8.GetBytes(jsonStr); return Encoding.UTF8.GetString(webClient.UploadData(cmdURL, data)); } private string Delete(string contentType, string accept, string cmdURL) { if (!string.IsNullOrEmpty(contentType)) { webClient.Headers.Add("Content-Type", contentType); } if (!string.IsNullOrEmpty(accept)) { webClient.Headers.Add("Accept", accept); } return webClient.UploadString(cmdURL,"DELETE", ""); } private string Put(string contentType, string accept, string cmdURL,string jsonStr) { if (!string.IsNullOrEmpty(contentType)) { webClient.Headers.Add("Content-Type", contentType); } if (!string.IsNullOrEmpty(accept)) { webClient.Headers.Add("Accept", accept); } byte[] data = Encoding.UTF8.GetBytes(jsonStr); // return webClient.UploadString(cmdURL, "PUT", jsonStr); return Encoding.UTF8.GetString(webClient.UploadData(cmdURL,"PUT", data)); } private void BtnDisAll_Click(object sender, EventArgs e) { //send("application/json", "Content-Type = application/json; Accept = N/A:", $"{serverIP}all"); var s1= Get("", "", $"{serverIP}/all"); djson(s1); RtbMsg.Text = s1; } List<Employee> employeeBuff = new List<Employee>(); private void djson(string jsondata) { employeeBuff.Clear(); var list= JsonConvert.DeserializeObject<List<Employee>>(jsondata); comboBox1.Items.Clear(); foreach(var m in list) { comboBox1.Items.Add(m.Id); } if (comboBox1.Items.Count > 0) { employeeBuff = list; comboBox1.SelectedIndex = 0; } } private void BtnAddNew_Click(object sender, EventArgs e) { if(TxtId.Text.Length<1 || TxtName.Text.Length<1 || TxtGrade.Text.Length<1 || TxtDepartment.MaxLength<1) { MessageBox.Show("检查id,name,grade,department是不是没填写"); return; } var data = new Employee { Id = TxtId.Text, Name = TxtName.Text, Grade = TxtGrade.Text, Department = TxtDepartment.Text }; string json = JsonConvert.SerializeObject(data); var s1 = Post("application/json", "", $"{serverIP}/", json); BtnDisAll_Click(null, null); } private void BtnDel_Click(object sender, EventArgs e) { var Id = comboBox1.Text; if (Id.Length < 1 ) { MessageBox.Show("没选择Id"); return; } var s1 = Delete("application/json", "", $"{serverIP}/{Id}"); BtnDisAll_Click(null, null); } private void BtnModify_Click(object sender, EventArgs e) { var Id = comboBox1.Text; if (Id.Length < 1) { MessageBox.Show("没选择Id"); return; } var data = new Employee { Id = TxtId.Text, Name = TxtName.Text, Grade = TxtGrade.Text, Department = TxtDepartment.Text }; string json = JsonConvert.SerializeObject(data); Put("application/json", "Content-Type = application/json; Accept = N/A:", $"{serverIP}/", json); BtnDisAll_Click(null, null); } private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { var Id = comboBox1.Text; if (string.IsNullOrEmpty(Id)) return; var idx=employeeBuff.FindIndex(s => s.Id == Id); if(idx>=0) { TxtId.Text = employeeBuff[idx].Id; TxtName.Text = employeeBuff[idx].Name; TxtGrade.Text = employeeBuff[idx].Grade; TxtDepartment.Text = employeeBuff[idx].Department; } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { webClient.Dispose(); } } }
来看看抓包的结果
下面是GetAllEmployees,取所有信息。
下面是新增了一条记录,“张飞”
下面是一个put,张飞改名为“张飞2”
下面是删除了005的记录

