REST

表述性状态转移(Representational State Transfer,REST),不是一种标准,而是一种软件架构风格。

基于REST的服务与基于SOAP的服务相比,性能、效率和易用性上都更高,而SOAP协议非常的复杂和不透明。REST受到越来越多的Web服务供应商欢迎。

REST的主要原则是:

1.网络上的所有事物都可被抽象为资源;

2.每个资源都有一个唯一的资源标识符URI;

3.使用标准方法操作资源;

4.所有的操作都是无状态的;

5.通过缓存来提高性能。

REST是基于Http协议的,任何对资源的操作行为都是通过Http协议来实现。Http把对一个资源的操作限制在4个方法以内:GET、POST、PUT和DELETE,这正是对资源CRUD操作的实现。

REST的资源表述形式可以是XML、HTML、JSON,或者其他任意的形式,这取决于服务提供商和消费服务的用户。
但是REST不是万能的。操作无状态也会带来巨大的安全问题,如何授权和验证用户?如果要求每次请求都包含完整的身份和验证信息,又如何避免信息泄漏?复杂的功能挑战架构的易用性,这就需要在性能与功能间权衡,究竟该用REST还是SOAP。

WebHttpBinding

      WebHttpBinding允许开发人员通过 HTTP请求(这些请求使用“Plain old XML”(POX) 样式消息,而不是使用基于 SOAP 的消息)来公开 Web 服务,可以很便利的实现REST。与其他绑定不同的是:必须使用WebHttpBehavior对服务的终结点进行配置。还要求使用WebGetAttribute或WebInvokeAttribute属性将各个服务操作映射到URI,同时定义调用和返回结果的消息格式。

服务契约

先定义服务契约。这里提供两个方法,分别采用GET和POST方式访问。

我们可以看到,与普通WCF服务契约不同的是,需要额外用WebGet或者WebInvoke指定REST访问的方式。另外还要指定消息包装样式和消息格式,默认的消息请求和响应格式为XML,若选择JSON需要显式声明。

UriTemplate用来将方法映射到具体的Uri上,但如果不指定映射,将映射到默认的Uri。比如采用Get访问的GetUser方法,默认映射是:/GetUser?Name={Name}&Position={Position}。

IContract

namespace Rest.Contract
{
 [DataContractFormat]
 [ServiceContract]
 public interface ITest
 {
 //[WebGet(UriTemplate = "/User/Get/{Name}/{Position}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
   [WebGet(UriTemplate = "/User/Get/{Name}/{Position}", BodyStyle = WebMessageBodyStyle.Bare)]
 [OperationContract]
 List<User> GetUser(string Name, string Position);
 
//[WebInvoke(Method = "POST", UriTemplate = "/User/Create", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
   [WebInvoke(Method = "POST", UriTemplate = "/User/Create", BodyStyle = WebMessageBodyStyle.Bare)]
[OperationContract]
 Result CreateUser(User u);
 }
 
[DataContract(Namespace = "http://rest-server/datacontract/user")]
 public class User
 {
 [DataMember]
 public long ID { get; set; }
 
 [DataMember] 
 public string Name { get; set; }
 
 [DataMember]
 public int Sex { get; set; }
 
 [DataMember]
 public string Position { get; set; }
 
 [DataMember]
 public string Email { get; set; }
 }
 
 [DataContract(Namespace = "http://rest-server/datacontract/result")]
 public class Result
 {
 [DataMember]
 public string Value { get; set; }
 }
 
 }

服务端

      这里最简单的实现GetUser和CreateUser两个方法的逻辑,关注点不在这里。

Service类

namespace Rest.Service
{
public class Test : ITest
{
/// <summary>
/// GET
/// </summary>
 /// <param name="Name"></param>
/// <param name="Position"></param>
/// <returns></returns>
  public List<User> GetUser(string Name, string Position)
{
List<User> userList = List.Where(u => u.Name == Name && u.Position == Position).ToList(); return userList;
} /// <summary>
/// POST
 /// </summary>
/// <param name="u"></param>
 /// <returns></returns>
   public Result CreateUser(User u)
 {
 Result result = new Result();
 
 if (!List.Any(user => user.ID == u.ID))
 List.Add(u);
 
 result.Value = u.ID.ToString();  return result;
 }
 
 /// <summary>
 /// 测试数据
 /// </summary>
   private static List<User> List
 {
 get
{
 List<User> list = new List<User>{
 new User{
 ID = ,
 Name = "wuhong",
 Sex = ,
 Position = "engineer",
 Email = "star_2345@qq.com"
 }
 };
 
 return list;
 }
 }
 }
 }

服务端的配置文件中只有一个特别处,必须使用WebHttpBehavior对服务的终结点进行配置。

Web.Config

<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webBinding">
</binding>
</webHttpBinding>
</bindings>
 <services>
<service name="Rest.Service.Test" behaviorConfiguration="testServiceBehavior">
 <endpoint address="" behaviorConfiguration="webBehavior" 
 binding="webHttpBinding" bindingConfiguration="webBinding" contract="Wuhong.Rest.Contract.ITest">
</endpoint>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="webBehavior">
<!--这里必须设置-->
<webHttp />
 </behavior>
</endpointBehaviors>
 <serviceBehaviors>
 <behavior name="testServiceBehavior">
 </behavior>
 </serviceBehaviors>
 </behaviors>
 </system.serviceModel>

客户端 
为了强调REST的通用性,客户端不用WCF的形式调用服务,而是用另外两种通用的方式: 
一是用C#编程直接HTTP访问,消息格式我们选XML;

二是用jquery实现GET和POST访问,消息格式我们选JSON。
先实现C#方式,我们封装一个Client类,实现HTTP的GET和POST方式。

namespace Rest.Client
 {
 public class RestClient
 {
 /// <summary>
 /// 构造函数
 /// </summary>
 /// <param name="baseUrl"></param>
   public RestClient(string baseUri)
{
his.BaseUri = baseUri;
} /// <summary>
/// 基地址
/// </summary>
 private string BaseUri; /// <summary>
/// Post调用
/// </summary>
/// <param name="data"></param>
/// <param name="uri"></param>
/// <returns></returns>
  public string Post(string data, string uri)
{
//Web访问对象
   string serviceUrl = string.Format("{0}/{1}", this.BaseUri, uri);
HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(serviceUrl);
 
 //转成网络流
   byte[] buf = UnicodeEncoding.UTF8.GetBytes(data);
 
 //设置
   myRequest.Method = "POST";
 myRequest.ContentLength = buf.Length;
 myRequest.ContentType = "text/html";
 
 // 发送请求
   Stream newStream = myRequest.GetRequestStream();
 newStream.Write(buf, , buf.Length);
 newStream.Close();
 
 // 获得接口返回值
   HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse();
 StreamReader reader = new StreamReader(myResponse.GetResponseStream(), Encoding.UTF8);
 
 string ReturnXml = HttpUtility.HtmlDecode(reader.ReadToEnd());
 
 reader.Close();
 myResponse.Close();
 
 return ReturnXml;
 }
 
 /// <summary>
 /// Get调用
 /// </summary>
 /// <param name="uri"></param>
 /// <returns></returns>
   public string Get(string uri)
 {
 //Web访问对象
   string serviceUrl = string.Format("{0}/{1}", this.BaseUri, uri);
 HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(serviceUrl);
 
 // 获得接口返回值
   HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse();
 StreamReader reader = new StreamReader(myResponse.GetResponseStream(), Encoding.UTF8);  string ReturnXml = HttpUtility.UrlDecode(reader.ReadToEnd());
 
reader.Close();
 myResponse.Close();  return ReturnXml;
 }
 
 }
 }

Client类

下面是主函数,按顺序调用两个接口,并显示返回值。需要注意XML约定的命名空间:

namespace Rest.Client
 {
 class Program
 {
 static void Main(string[] args)
 {
 //初始化
   RestClient client = new RestClient(ClientConfiguration.ServiceUrl);
 
 //Get
   string uriGet = string.Format("User/Get/{0}/{1}", "wuhong", "engineer");
 string retGet = client.Get(uriGet);
 
 Console.WriteLine(retGet);
 
 //Post
   string uriPost = "User/Create";
 string data = "<User xmlns=\"http://rest-server/datacontract/user\"><ID>19735</ID><Name>wuhong</Name><Sex>1</Sex><Position>engineer</Position><Email>star_2345@qq.com</Email></User>";
  
 string retPost = client.Post(data, uriPost);
 
 Console.WriteLine(retPost);
 
 Console.ReadLine();
 }
 }
 
 }
  

Client Main函数

接下来实现javascript方式。

这里采用jquery访问REST服务,为了javascript操作数据的便利,消息格式选择JSON,可以忽略数据契约的命名空间。不过需要服务契约做一点修改,显式指定请求或响应消息的格式,例如:

1 [WebInvoke(Method = "POST", UriTemplate = "User/Create", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]

Html代码:

<html>
<head>
<script src="jquery-1.3.2.min.js" type="text/javascript"></script>
<script type="text/javascript">
function HttpGet() {
$.get( http://doxt-wuhong/Wuhong.Rest.Web/TestService.svc/User/Get/wuhong/engineer ,
 function(data) {
 $("#TextGet").val(data);
 });
}
 function HttpPost() {
 var str = "{ \"Email\": \"star_2345@qq.com\", \"ID\": 19735, \"Name\": \"wuhong\", \"Position\": \"engineer\", \"Sex\": 1 }";
 $.ajax({
 type: "POST",
contentType: "application/json",
url:  http://doxt-wuhong/Wuhong.Rest.Web/TestService.svc/User/Create,
data: str,
success: function(data) {
$("#TextPost").val(data);
}
});
}
</script>
<style type="text/css">
#TextGet
{
width: 700px;
}
#TextPost
{
width: 700px;
}
</style>
</head>
<body>
<input id="ButtonGet" type="button" value="GET" onclick="HttpGet()" />
<input id="TextGet" type="text" />
<p/> 
<input id="ButtonPost" type="button" value="POST" onclick="HttpPost()" />
 <input id="TextPost" type="text" />
</body>
  </html>

Html代码

WCF实现REST服务的更多相关文章

  1. WCF初探-10:WCF客户端调用服务

    创建WCF 服务客户端应用程序需要执行下列步骤: 获取服务终结点的服务协定.绑定以及地址信息 使用该信息创建 WCF 客户端 调用操作 关闭该 WCF 客户端对象 WCF客户端调用服务存在以下特点: ...

  2. 跟我一起学WCF(11)——WCF中队列服务详解

    一.引言 在前面的WCF服务中,它都要求服务与客户端两端都必须启动并且运行,从而实现彼此间的交互.然而,还有相当多的情况希望一个面向服务的应用中拥有离线交互的能力.WCF通过服务队列的方法来支持客户端 ...

  3. 真实世界:使用WCF扩展记录服务调用时间

    WCF 可扩展性 WCF 提供了许多扩展点供开发人员自定义运行时行为. WCF 在 Channel Layer 之上还提供了一个高级运行时,主要是针对应用程序开发人员.在 WCF 文档中,它常被称为服 ...

  4. WCF入门(三)---WCF与Web服务/Web Service

    下面列出了WCF和Web服务之间存在一些重大差异. 属性:WCF服务是通过定义ServiceContract和OperationContract属性,而在Web服务,WebService和WebMet ...

  5. WCF中队列服务详解

    WCF中队列服务详解 一.引言 在前面的WCF服务中,它都要求服务与客户端两端都必须启动并且运行,从而实现彼此间的交互.然而,还有相当多的情况希望一个面向服务的应用中拥有离线交互的能力.WCF通过服务 ...

  6. 使用WCF扩展记录服务调用时间

    随笔- 64  文章- 0  评论- 549  真实世界:使用WCF扩展记录服务调用时间   WCF 可扩展性 WCF 提供了许多扩展点供开发人员自定义运行时行为. WCF 在 Channel Lay ...

  7. WCF心跳判断服务端及客户端是否掉线并实现重连接

    WCF心跳判断服务端及客户端是否掉线并实现重连接 本篇文章将通过一个实例实现对WCF中针对服务端以及客户端是否掉线进行判断:若掉线时服务器或客户端又在线时将实现自动重连:将通过WCF的双工知识以及相应 ...

  8. WCF系列教程之WCF客户端调用服务

    1.创建WCF客户端应用程序需要执行下列步骤 (1).获取服务终结点的服务协定.绑定以及地址信息 (2).使用该信息创建WCF客户端 (3).调用操作 (4).关闭WCF客户端对象 二.操作实例 1. ...

  9. WCF扩展记录服务调用时间

    WCF 提供了许多扩展点供开发人员自定义运行时行为. WCF 在 Channel Layer 之上还提供了一个高级运行时,主要是针对应用程序开发人员.在 WCF 文档中,它常被称为服务模型层(Serv ...

  10. WCF客户端获取服务端异常[自定义异常]

    引言 经过不断的摸索,询问/调试,终于学会了关于WCF客户端与服务端之间异常的处理机制,在此来记录自己的成果,用于记录与分享给需要的伙伴们. 首先感谢[.NET技术群]里群主[轩]的大力帮助,如有需要 ...

随机推荐

  1. Linux运维-zabbix_agent最新版的yum安装

    agentd端可以直接使用yum来进行安装 rpm -ivh http://repo.zabbix.com/zabbix/2.4/rhel/6/x86_64/zabbix-release-2.4-1. ...

  2. 怎样使用Chrome模拟手机浏览器測试移动端网站

    作者:zhanhailiang 日期:2014-10-10 环境说明: Chrome 37.0.2062.124 m 1. 通过[菜单→工具→开发人员工具|Javascript控制台]或[快捷键Ctr ...

  3. 《Tensorflow技术解析与实战》第四章

    Tensorflow基础知识 Tensorflow设计理念 (1)将图的定义和图的运行完全分开,因此Tensorflow被认为是一个"符合主义"的库 (2)Tensorflow中涉 ...

  4. F110的几个功能

    1.F-59, 没有找到函数, 使用BDC BAPI_ACC_DOCUMENT_POST 必须创建有借贷2 line 的凭证,需求要参考原始的SA类型凭证, 创建一个单条的 科目 = 供应商 的凭证, ...

  5. 剑指offer 面试60题

    面试60题 题目:把n个骰子扔在地上,所有骰子朝上一面的点数之和为s.输入n,打印出s的所有可能的值出现的概率. 解决代码:

  6. Python函数之—— 装饰器(Day13)

    一.什么是装饰器 顾名思义,装饰器指为其他函数添加新功能 装饰器定义:本质就是函数,功能是为其他函数添加新功能 二.装饰器需要遵循的原则 1.不修改被装饰函数的源代码(开放封闭原则) 2.为被装饰函数 ...

  7. sql server分区

    1. 创建分区 分区步骤:1.创建分区函数  2.创建分区架构 3.创建分区索引(聚集)   --1. 创建分区函数 DECLARE @dt datetime SET @dt = '20030901' ...

  8. Spring:笔记整理(1)——HelloWorld

    Spring:笔记整理(1)——HelloWorld 导入JAR包: 核心Jar包 Jar包解释 Spring-core 这个jar 文件包含Spring 框架基本的核心工具类.Spring 其它组件 ...

  9. $SVN代码版本管理工具的使用

    SVN是一种代码版本管理工具,具有可视化的操作界面,使用简便,和git的功能类似.下面总结一下SVN的基本用法: 1.安装SVN软件,和安装一般的软件的步骤差不多,这里使用的版本是TortoiseSV ...

  10. 011_用eclipse打开hadoop1.2.1源码出现The method getSplits(JobConf, int) is undefined for the type Object错误

    出现的错误截图: 此时Eclipse使用的jdk1.8,将编译环境改成jdk1.7就行了,解决. 没问题了. 下面观点是参考如下网址,未经验证.参考:http://blog.csdn.net/onei ...