勇哥注:
这里补充几个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的记录



少有人走的路



















