关于本文

这篇文章的目的就是向大家阐述如何在.net framework 4.0中创建RestFul Service并且使用它。

什么是web Services,什么是WCF

首先讲到的是web Service, 它是一种能够让客户端程序在web页面上通过HTTP协议请求需要数据的部件。我们可以用Asp.net创建普通的Web Services并且让这些Services能够被客户端程序所调用。

其次说到的是Web Services,它是一个编程平台,它能够通过遵循Simple Object Access Protocol (SOAP)方式来接收或者是发送数据。

然后就是WCF了,它是能够编写基于service-oriented architecture (SOA)服务的编程模型。通过它,开发人员可以编写出跨平台的,安全的,可靠的解决方案出来。

WCF可以为各种各样的客户提供集中式的运算服务,客户可以调用多个服务,同时,相同的服务也能够被多个用户所调用。

在创建我们的WCF项目之前,我们最好能够看一下这篇文章:Introduction of Window Communication Foundation

下面是WebServices和WCF Services的数据比对:

Web Services WCF Services
Web Services能够用来开发基于SOAP的消息发送和接收的应用,这些消息是XML格式的,这些xml格式的消息可以通过.net Framework 提供的工具类来进行序列化。这种可以能够自动生成元数据(metadata)来描述Web Services的技术,我们称为: The Web Services Description Language ( WSDL )(注意:WSDL是一种描述WebService服务以及说明如何与Web服务进行通信的XML语言。) WCF Services能够用来开发基于多种协议格式的应用,SOAP只是其默认的格式。它的消息格式也是使用XML,但是序列化这些xml数据的方式有很多种。它可以通过WSDL自动的生成能够描述其应用的元数据,同时也能够利用工具来生成。

对于WCF来说,发送消息不局限于HTTP方式,TCP或者是其他的网络协议也可以。并且在这些协议中进行切换,也是轻而易举的事儿。它除了能够放到服务器上运行之外,还能够支持宿主寄存,它同时能够非常容易的支持最新的Web Services标准(SOAP 1.2 and WS-*),
它除了能够以SOAP的方式发送数据,也能够用其他的方式来进行。

XmlSerializer DataContractSerializer
当Asp.net从服务器发送数据或者是接收客户端传来的数据的时候,它是利用XmlSerializer来进行数据的转换的。 DataContractSerializer表明,有0个或者是多个属性或者是字段需要被序列化;而DataMemberAttribute 则表明一个指定的属性或者字段需要被序列化。DataContractAttribute可以被应用到具有Public 或者是Private的属性或者字段中,所有被标记上DataContractAttribute的属性或者字段,在WCF中被称作是数据契约(DataContracts),他们将会被DataContractSerializer序列化成XML结构。
System.Xml.Serialization下的XmlSerializer类及其各种特性,可以让各种.net framework 类型转换成xml结构,它对XML提供了非常好的控制。 DataContractSerializer,DataContractAttribute,DataMemberAttribute对XML提供了很有限的控制,所以,你只能够通过命名空间去甄别。你可以利用DataContractSerializer去操控除了xml之外的各种数据结构,这种没有添加过多限制的操作,能够让DataContractSerializer更易于操控。
和DataContractSerializer相比,它的性能差一些 DataContractSerializer性能好一些,性能一般会提升10%左右
XmlSerializer不会预知应该或者是不应该包含什么字段或者是属性到XML中。 利用DataMemberAttribute,在DataContractSerializer进行序列化的时候,可以非常明确地知道数据结构。
XmlSerializer只能够将Public类型的成员进行序列化。 DataContractSerializer不会类型成员的属性进行检测。
只有继承自IEnumerable或者是ICollection接口的集合类型才能够被序列化成XML。 DataContractSerializer可以将任何.net类型转换成xml结构.
继承自IDictionary接口的类,比如说Hashtable,是不能够被序列化成XML的。 继承自IDictionary接口的类,比如说Hashtable,能够被序列化成XML。
XmlSerializer不支持版本控制 DataContractSerializer支持版本控制。
XmlSerializer对序列化的xml语义结构基本相同。 DataContractSerializer序列化为xml的时候,需提供明确的命名空间。

什么是REST和 RESTFul?

Representational State Transfer(REST)在2000年被Roy Fielding提出。它是利用World Wide Web的相关技术以及协议来构建大规模的网络软件的构架方式。它意在说明,数据资源是可以被定义,被发布的,尤其是在消息交换的简易性和可扩展性方面。

早在2000年的时候, 制定HTTP规范的首席作者之一,Roy Fielding,写了一篇名为“Architectural Styles and the Design of Network-based Software Architectures.”的博士论文,在文中,作者如是说。

REST,一种能够构建分布式超媒体驱动程序的构架方式,它包括通过利用标准的HTTP Verbs(GET, POST, PUT, and DELETE)来定义资源,从而构建Resource-Oriented Architecture (ROA)程序。这种构建可以通过Uniform Resource Identifier(URI)发布给使用者。

REST 是不绑定任何技术或者是平台的。它只是用来把工作设计成能像Web那样。人们经常把这种方式定义为“RESTFul Services”,但是事实上,利用REST 构架方式开发的WCF Services,也能够被称作是RESTFul Services。

WCF Services RESTFul Services
各自的网络协议之间,需要创建端点。 可以通过HTTP Web方式来进行数据的收发操作。
需要Remote Procedural Call(RPC)的支持 需要定义基于HTTP的统一接口
客户端需要添加对服务端的引用 不需要客户端添加对服务端的引用

关于案例代码?

在WCF 中,REST,其实就是一系列的.net framework 类和visual studio的一些特性和模板,通过这些东西,用户可以创建并且使用基于REST方式的WCF服务。这些服务是需要.net 3.5 SP1中的WCF Web编程模型支持的。在后面的附件中,已经包含了所有的源代码,示例,以及单元测试。

创建基础的RESTFul Service

步骤一:

利用Visual Studio 2010创建一个新的WCF 项目:

步骤二:

删掉自带的IService1.cs以及Service1.svc文件,同时创建以下两个文件:IRESTService.cs &RESTService.cs.

步骤三:

在IService.cs文件中创建Person 实体类以及一些简单的属性,同时加上DataContract & DataMember特性以便于DataContractSerialization进行序列化:

[DataContract]
public class Person
{
    [DataMember(order=1)]
    public string ID;
    [DataMember(order=2)]
    public string Name;
    [DataMember(order=3)]
    public string Age;
    [DataMember(order=4)]
    public string Address;
}
注意,一定要加上Order=*,否则,客户端向服务端添加数据的时候,会出现某些数据字段没有值的情况。

步骤四:

在IRESTService接口文件中创建方法,利用ServiceContract和OperationContrat进行修饰:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Web; namespace RESTFulDaemon
{
[ServiceContract]
public interface IRestSerivce
{
//POST operation
[OperationContract]
[WebInvoke(
UriTemplate = ""
,ResponseFormat = WebMessageFormat.Xml
,RequestFormat = WebMessageFormat.Xml
,Method = "POST"
)]
Person CreatePerson(Person createPerson); //Get Operation
[OperationContract]
[WebGet(
UriTemplate = ""
, ResponseFormat = WebMessageFormat.Xml
, RequestFormat = WebMessageFormat.Xml
)]
List<Person> GetAllPerson(); [OperationContract]
[WebGet(
UriTemplate = "{id}"
, ResponseFormat = WebMessageFormat.Json
, RequestFormat = WebMessageFormat.Json
)]
Person GetAPerson(string id); //PUT Operation
[OperationContract]
[WebInvoke(
UriTemplate = ""
, ResponseFormat = WebMessageFormat.Json
, RequestFormat = WebMessageFormat.Json
, Method = "PUT"
)]
string UpdatePerson(Person updatePerson); //DELETE Operation
[OperationContract]
[WebInvoke(
UriTemplate = ""
, ResponseFormat = WebMessageFormat.Json
, Method = "DELETE"
)]
string DeletePerson(string id);
}
}

代码中,可以利用JSON来组织数据,也可以利用XML来组织数据

步骤五:
WebGet 操作只是从服务端接收数据的操作,它可以被REST编程模型所调用。它会和OperationContact一起被应用到方法上,并且会带有UriTemplate属性和HTTP中的Get操作。
WebInvoke 可以引发服务端操作,它可以被REST编程模型所调用。当它具有UriTemplate定义并且拥有传输方式(比如, HTTP POST, PUT, or DELETE),同时和OperationContact放在一块的时候,就表明当前方法支持的是不同类型的HTTP传输方式。默认是POST方式。
下面 是具体说明:
Person CreatePerson(Person createPerson);
//It is basically insert operation, so WebInvoke HTTP POST is used
 
List<person> GetAllPerson();
Person GetAPerson(string id);
//These two methods retrieve the information, so WebGet (HTTP Get) is used
 
 Person UpdatePerson(Person updatePerson);
//This method updates the available information, so WebInvoke HTTP PUT is used
 
void DeletePerson(string id);
//This method deletes the available information, 
//so WebInvoke HTTP DELETE is used
 
步骤六:
实现在RESTService类中实现IRESTService接口:
public class RESTService:IRestSerivce
{
List<Person> persons = new List<Person>();
private static int tCount = ;
public Person CreatePerson(Person person)
{
tCount++;
person.ID = tCount.ToString();
persons.Add(person);
return person;
}
public List<Person> GetAllPerson()
{
return persons.ToList();
}
public Person GetAPerson(string id)
{
return persons.FirstOrDefault(e => e.ID.Equals(id));
}
public string UpdatePerson(Person updatePerson)
{
Person p = persons.FirstOrDefault(e => e.ID.Equals(updatePerson.ID));
p.Name = updatePerson.Name+"[updated]";
p.Age = updatePerson.Age+"[updated]";
p.Address = updatePerson.Address+"[updated]";
return "Updated success";
}
public string DeletePerson(string id)
{
persons.RemoveAll(e => e.ID.Equals(id));
return "success";
}
}
步骤七:
为了让这个服务能够在ASP.NET兼容模式下运行,我们需要利用AspNetCompatibilityRequirements特性来修饰RESTService类:
 [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class RESTService:IRestSerivce
{
//Code here
}
所以目前的代码如下:
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Activation;
using System.ServiceModel;
using System; namespace RESTFulDaemon
{
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class RESTService:IRestSerivce
{
List<Person> persons = new List<Person>();
private static int tCount = ;
public Person CreatePerson(Person person)
{
tCount++;
person.ID = tCount.ToString();
persons.Add(person);
return person;
}
public List<Person> GetAllPerson()
{
return persons.ToList();
}
public Person GetAPerson(string id)
{
return persons.FirstOrDefault(e => e.ID.Equals(id));
}
public string UpdatePerson(Person updatePerson)
{
Person p = persons.FirstOrDefault(e => e.ID.Equals(updatePerson.ID));
p.Name = updatePerson.Name+"[updated]";
p.Age = updatePerson.Age+"[updated]";
p.Address = updatePerson.Address+"[updated]";
return "Updated success";
}
public string DeletePerson(string id)
{
persons.RemoveAll(e => e.ID.Equals(id));
return "success";
}
}
}
在上述的类中,类前面被打上了ServiceBehavior特性,主要是用来说明这服务端只会存在这个类的一个实例.
 

步骤八:
如果要使用这个RESTFul Service应用,那么这个服务就必须得被寄宿.所以我们必须做如下的更改才能让RESTService跑起来.
在Web.config文件中,移除掉<system.servicemodel>下面的所有代码,并且加入如下的新的代码:
<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true">
    </serviceHostingEnvironment>
    <standardEndpoints>
      <webHttpEndpoint>
        <standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"></standardEndpoint>
      </webHttpEndpoint>
    </standardEndpoints>
  </system.serviceModel>

其中,<serviceHostingEnvironment>标记用于制定当前应用将运行在Asp.net兼容模式下.

<standardEndpoints> 标记用于为RESTFul应用获取WebHelp.

然后,在Global.asax文件中,我们需要在Application_Start事件中添加如下代码:

RouteTable.Routes.Add(new ServiceRoute("RestService", new WebServiceHostFactory(), typeof(RESTSerivce)));  
 
步骤九:
运行这个应用:
我们可以看到如下的页面:
当运行http://localhost:****/RestService/Help的时候,我们可以看到如下页面:
 
如何使用RESTFul Service呢?

客户端编程

这里我先贴出客户端代码:

using System.Text;
using System.Windows;
using System.Net;
using System.Xml.Serialization;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Diagnostics;
using System.Runtime.Serialization.Json;
using System.Collections.Generic; namespace ConsumingDaemon
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
} private void btnCreate_Click(object sender, RoutedEventArgs e)
{
//Client entity
Person p = new Person();
p.ID = "";
p.Name = "scy";
p.Age = "";
p.Address = "China HeNan"; HttpWebRequest request = WebRequest.Create("http://localhost:11126/RESTService/") as HttpWebRequest;
request.KeepAlive = false;
request.Method = "POST"; StringWriter sw = new StringWriter();
XmlSerializer xs = new XmlSerializer(p.GetType());
xs.Serialize(sw,(object)p);
string result = sw.ToString(); //generate xml that required
result = result.Replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n",string.Empty);
Regex regex = new Regex("<Person.*?>");
Match m = regex.Match(result);
if (m.Success)
{
result = result.Replace(m.Value,"<Person xmlns=\"http://schemas.datacontract.org/2004/07/RESTFulDaemon\">");
} AutoResetEvent autoResetEvent = new AutoResetEvent(false); byte[] buffer = Encoding.Default.GetBytes(result);
request.ContentLength = buffer.Length;
request.ContentType = "text/xml";
request.BeginGetRequestStream((iar) =>
{
Stream PostData = request.EndGetRequestStream(iar);
PostData.Write(buffer, , buffer.Length);
PostData.Close();
autoResetEvent.Set();
},null); autoResetEvent.WaitOne(); HttpWebResponse resp = request.GetResponse() as HttpWebResponse;
StreamReader loResponseStream = new StreamReader(resp.GetResponseStream(), Encoding.Default); string Response = loResponseStream.ReadToEnd();
MessageBox.Show(Response);
loResponseStream.Close();
resp.Close(); } private void btnGetAll_Click(object sender, RoutedEventArgs e)
{
WebClient client = new WebClient();
client.Headers["Content-type"] = "application/json"; // invoke the REST method
byte[] data = client.DownloadData("http://localhost:11126/RESTService"); // put the downloaded data in a memory stream
MemoryStream ms = new MemoryStream();
ms = new MemoryStream(data); // deserialize from json
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(List<Person>)); List<Person> result = ser.ReadObject(ms) as List<Person>; string resultEx = string.Empty;
foreach (var r in result)
{
resultEx += r.ID + "--";
}
MessageBox.Show(resultEx);
} private void btnGetOne_Click(object sender, RoutedEventArgs e)
{
string id = txtGet.Text;
Person result = GetOneByID(id);
MessageBox.Show(result.ID + "|" + result.Name + "|" + result.Age + "|" + result.Address);
} private Person GetOneByID(string id)
{
WebClient client = new WebClient();
client.Headers["Content-type"] = "application/json"; byte[] data = client.DownloadData("http://localhost:11126/RESTService/" + id); MemoryStream ms = new MemoryStream();
ms = new MemoryStream(data); DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Person));
Person result = ser.ReadObject(ms) as Person;
return result;
} private void btnUpdateOne_Click(object sender, RoutedEventArgs e)
{
#region The XML way
//string id = txtUpdate.Text; ////Client entity
//Person p = new Person();
//p.ID = "3";
//p.Name = "scy1";
//p.Age = "28";
//p.Address = "Amearican Person"; //HttpWebRequest request = WebRequest.Create("http://localhost:11126/RESTService/") as HttpWebRequest;
//request.KeepAlive = false;
//request.Method = "PUT"; //StringWriter sw = new StringWriter();
//XmlSerializer xs = new XmlSerializer(p.GetType());
//xs.Serialize(sw, (object)p);
//string result = sw.ToString(); ////generate xml that required
//result = result.Replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n", string.Empty);
//Regex regex = new Regex("<Person.*?>");
//Match m = regex.Match(result);
//if (m.Success)
//{
// result = result.Replace(m.Value, "<Person xmlns=\"http://schemas.datacontract.org/2004/07/RESTFulDaemon\">");
//} //AutoResetEvent autoResetEvent = new AutoResetEvent(false); //byte[] buffer = Encoding.Default.GetBytes(result);
//request.ContentLength = buffer.Length;
//request.ContentType = "text/xml";
//request.BeginGetRequestStream((iar) =>
//{
// Stream PostData = request.EndGetRequestStream(iar);
// PostData.Write(buffer, 0, buffer.Length);
// PostData.Close();
// autoResetEvent.Set();
//}, null); //autoResetEvent.WaitOne(); //HttpWebResponse resp = request.GetResponse() as HttpWebResponse;
//StreamReader loResponseStream = new StreamReader(resp.GetResponseStream(), Encoding.Default); //string Response = loResponseStream.ReadToEnd();
//MessageBox.Show(Response);
//loResponseStream.Close();
//resp.Close();
#endregion #region The JSON way
string id = txtUpdate.Text; Person p = GetOneByID(id);
WebClient client = new WebClient();
client.Headers["Content-type"] = "application/json"; // serialize the object data in json format
MemoryStream ms = new MemoryStream();
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Person));
ser.WriteObject(ms, p); byte[] resultData = client.UploadData("http://localhost:11126/RESTService/", "PUT", ms.ToArray()); MessageBox.Show(Encoding.Default.GetString(resultData));
#endregion
} private void btnDeleteOne_Click(object sender, RoutedEventArgs e)
{
string id = txtDelete.Text;
WebClient client = new WebClient();
client.Headers["Content-type"] = "application/json";
// serialize the object data in json format
MemoryStream ms = new MemoryStream();
DataContractJsonSerializer ser =new DataContractJsonSerializer(typeof(string));
ser.WriteObject(ms, id); // invoke the REST method
byte[] data = client.UploadData("http://localhost:11126/RESTService/", "DELETE", ms.ToArray()); MessageBox.Show(Encoding.Default.GetString(data));
}
}
}

下面是利用WPF写的Client程序,当点击“创建用户”按钮的时候,显示的结果:

当点击“获取所有用户”的时候,会把所有用户的ID号给获取到:
当点击“获取某个用户”按钮时的结果(2号用户信息被获取):
当点击“更新某个用户”按钮时的结果(3号用户信息被更新):

当点击“删除某个用户”按钮时候的显示结果(4号用户被删除):

Client端的显示效果如下:

源码下载

Click here to get source code

特别说明:本文翻译自Create and Consume RESTFul Service in .NET Framework 4.0一文,但是文中的示例已经被我重写了一遍,能够充分展示在客户端的CRUD的过程。
 


 

 

基于.Net FrameWork的 RestFul Service的更多相关文章

  1. 构建基于WCF Restful Service的服务

    前言 传统的Asmx服务,由于遵循SOAP协议,所以返回内容以xml方式组织.并且客户端需要添加服务端引用才能使用(虽然看到网络上已经提供了这方面的Dynamic Proxy,但是没有这种方式简便), ...

  2. [转]构建基于WCF Restful Service的服务

    本文转自:http://www.cnblogs.com/scy251147/p/3566638.html 前言 传统的Asmx服务,由于遵循SOAP协议,所以返回内容以xml方式组织.并且客户端需要添 ...

  3. step6----->往工程中添加spring boot项目------->修改pom.xml使得我的project是基于spring boot的,而非直接基于spring framework

    文章内容概述: spring项目组其实有多个projects,如spring IO platform用于管理external dependencies的版本,通过定义BOM(bill of mater ...

  4. 在Apworks数据服务中使用基于Entity Framework Core的仓储(Repository)实现

    <在ASP.NET Core中使用Apworks快速开发数据服务>一文中,我介绍了如何使用Apworks框架的数据服务来快速构建用于查询和管理数据模型的RESTful API,通过该文的介 ...

  5. C# 服务端篇之实现RestFul Service开发(简单实用)

    一.RestFul简介 REST(Representational State Transfer 通常被翻译为“表述性状态传输”或者“表述性状态转移”)是RoyFielding提出的一个描述互联系统架 ...

  6. Apworks框架实战(六):使用基于Entity Framework的仓储基础结构

    在前面的章节中,我们已经设计了一个简单的领域模型,接下来我们希望能够实现领域模型的持久化及查询.在Apworks中,实现了面向Entity Framework.NHibernate以及MongoDB的 ...

  7. 在IIS8.5的环境下配置WCF的Restful Service

    今天在客户的环境中(Windows Server 2012 R2 + IIS 8.5)搭建Call WCF Restful Service的功能,发现了几个环境配置的问题,记录如下: 1):此环境先安 ...

  8. XData -–无需开发、基于配置的数据库RESTful服务,可作为移动App和ExtJS、WPF/Silverlight、Ajax等应用的服务端

    XData -–无需开发.基于配置的数据库RESTful服务,可作为移动App和ExtJS.WPF/Silverlight.Ajax等应用的服务端   源起一个App项目,Web服务器就一台,已经装了 ...

  9. 使用spring boot+mybatis+mysql 构建RESTful Service

    开发目标 开发两个RESTful Service Method Url Description GET /article/findAll POST /article/insert 主要使用到的技术 j ...

随机推荐

  1. Android直方图递增View

    继上次分析实现Android自定义View之扇形图之后,自己又画了下面的这个递增直方图,本来是想做个静态的直方图就完了,结果想想静态的没啥趣味,于是就加了递增 1 从分析最终效果 界面上要展现的东西有 ...

  2. Maven&&Ant使用

    “使用操作系统环境为CentOS-6.5” Ant使用 Maven使用 “Maven是一个项目管理和综合工具.Maven提供了开发人员构建一个完整的生命周期框架.开发团队可以自动完成项目的基础工具建设 ...

  3. 使用htmlunit在线解析网页信息

    前言 最近工作上遇到一个问题,后端有一个定时任务,需要用JAVA每天判断法定节假日.周末放假,上班等情况, 其实想单独通过逻辑什么的去判断中国法定节假日的放假情况,基本不可能,因为国家每一年的假期可能 ...

  4. Effective Java 13 Minimize the accessibility of classes and members

    Information hiding is important for many reasons, most of which stem from the fact that it decouples ...

  5. Oracle 数据库二 基本查询

    查询当前用户:show user 查看当前用户下的表:select *from tab; 设置行宽: show linesize;(查看行宽)     set linesize 120;(设置行宽) ...

  6. python模块调用

    1.某个模块1被调用到另一个模块2的时候,当不想模块1中的某些代码在模块2中执行时,可以在模块一种添加如下代码: if    __name__  ==     "__main__" ...

  7. cocos2d-x之猜数字游戏

    bool HelloWorld::init() { if ( !Layer::init() ) { return false; } visibleSize = Director::getInstanc ...

  8. Error: Could not access the Package Manager. Is the system running?

    最近在搭建cordova,android 开发环境,安装android studio之后创建一个demo之后,运行想看一下效果,在运行过程中创建一个虚拟机(arm)的,等了有1分钟左右,再次运行程序, ...

  9. xcode 自定义include路径

  10. 在AngularJS中实现自定义表单验证

    除了一些已经定义好了的验证(例如 必填项.最小长度.最大长度)之外,更常用的,还是需要我们自己定义表单验证,这样才能对于项目中遇到的很多非常规问题给出自己的合适的解决方案. 在表单中控制变量 表单的属 ...