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. 【转】 HMC与VIOS对新LPAR提供存储与网络虚拟化的支持

    前面的几篇博文的操作环境都是在IVM下,IVM可以看作是VIOS的一部分,或者是对VIOS功能的一个扩展,一个IVM只能管理1台物理服务器,而HMC则是一对多.在有HMC来管理物理服务器的情形下,VI ...

  2. 处理 Java 的“Cannot allocate memory”错误

    今天在配置 DCA 服务器的时候,检验 java 版本的时候忽然遇到了一个 Cannot allocate memory 错误 [root@elcid-prod1 ~]# java -version ...

  3. python面试题(五)

    1 谈谈你对面向对象的理解? 面向对象的编程---object oriented programming,简称:OOP,是一种编程的思想.OOP把对象当成一个程序的基本单元,一个对象包含了数据和操作数 ...

  4. Ubuntu部署jmeter

    一:ubuntu部署jdk 1:先下载jdk-8u74-linux-x64.tar.gz,上传到服务器,这里上传文件用到了ubuntu 下的 lrzsz. ubuntu下直接执行 sudo apt-g ...

  5. Win10在右键菜单添加“在此处打开命令窗口”设置项

    Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\Directory\shell\OpenCmdHere] @="在此处打开命令 ...

  6. 剑指offer 面试67题

    面试67题: 题目: 链接:https://www.nowcoder.com/questionTerminal/1277c681251b4372bdef344468e4f26e?commentTags ...

  7. Log level with log4j and Spark

    Log Level Usages OFF This is the most specific, which allows no logging at all FATAL This is the mos ...

  8. pip3命令报错Fatal error in launcher: Unable to create process using '"d:\old_files\py3.6\python.exe" "E:\py3.6\Scripts\pip3.exe" list'

    cmd输入pip3 list命令报错 Fatal error in launcher: Unable to create process using '"d:\old_files\py3.6 ...

  9. iOS 点击注释图标 弹出对应解释

    需求:如题目  接上一篇的开发内容 效果图: 这种情况存在tableView 的一个cell中. 要点 1,  弹出的对应解释 要在可视区域,并且小尖角 要指着 图片 2,  文本不能过高 有极大高度 ...

  10. 如何安装/更新ruby,安装cocoapods,为开发做好准备!(2016年12月07日更新内容)

    一:首先来说一下如何安装/更新ruby: 一般情况下,即使是新买的Mac电脑也会安装有ruby,可以在终端中键入一下命令查看ruby版本 ruby -v 正常情况下下面会打印出ruby的版本信息,如果 ...