Wcf Rest服务,是一种Web服务。Web服务是一种面向服务的架构技术,通过标准的Web协议提供服务(Web Service),它的好处是可以让不同的平台应用实现互连,进而达到数据的交换。Web服务可以说是一组工具的集合,可以用多种方式来调用。比较常用的方式有三种:远程过程调用(RPC),面向服务架构(SOA)以及表述性状态转换(REST)。
这里面,我们只介绍REST的实现,结合.net的WCF来实现。
在开始这一讲之前,还需要再补一点概念方面的内容来帮助大家进一步理解里面的知识点。REST 从资源的角度来观察整个网络,整个网络的资源由URI确定,而客户端的应用只需要通过URI来获取资源的表征。获得这些表征致使这些应用程序转变了其状态。随着不断获取资源的表征,客户端应用不断地在转变着其状态,这就是所谓的表征状态转移(Representational State Transfer)。其实这里面的概念说直白些,就是通过URI的渠道,我们能得到我们想要的资源。资源的方式可以是XML、JSON或者是HTML,取决于读者是机器还是人,是消费web服务的客户软件还是web浏览器。当然也可以是任何其他的格式。对于符合 REST principles (REST原则)的系統称做RESTful。它是一个使用HTTP协议并遵循REST原则的Web服务。它从以下三个方面的资源进行定义:
(1)URI{统一资源标识符(Uniform Resource Identifier,或URI)是一个用于标识某一互联网资源名称的字符串},比如:http://example.com/resources/。
(2)Web服务接受与返回的互联网媒体类型,比如:JSON,XML ,YAML,HTML 等。
(3)Web服务在该资源上所支持的一系列请求方法(比如:PUT,DELETE,POST,GET)。标准操作的话,这里面的PUT,DELETE,POST,GET对应的是增,删,改,查。意思就是你对资源的请求方法(Method)是什么操作,比如是想增加资源的话,可以用PUT方法;是想删除资源的话,可以用DELETE方法;是想修改资源的话,可以用PUT方法;是想查询资源的话,你可以用GET方法;
上面的内容基本上都是从维基里面参考而来,如果想了解更多的话,可以参照链接维基百科-REST
好了,下面开始本文的Wcf Rest编程方面的内容。这里面只对Wcf的知识进行简单解释,如果没有学习过Wcf知识的话,麻烦大家查找相关的内容。
首先,先上一张工程的结构图:
采用创建WCF默认的以接口为导向的编程,这里面我定义的接口文件为IOperationUser.cs,源码如下所示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Runtime.Serialization;
using System.IO;
namespace WcfRest
{
[ServiceContract]
public interface IOperationUser
{
[OperationContract]
[WebGet(UriTemplate = "/username/{username}/password/{password}")]
User GetUser(string username, string password);
[OperationContract]
[WebInvoke(UriTemplate = "/user", Method = "POST")]
User PostUser(Stream postStream);
[OperationContract]
[WebInvoke(UriTemplate = "/user", Method = "PUT")]
User PutUser(Stream putStream);
[OperationContract]
[WebInvoke(UriTemplate = "/username/{username}", Method = "DELETE")]
User DeleteUser(string username);
}
[DataContract(Name = "user", Namespace = "")]
public class User
{
[DataMember(IsRequired=false,Order=0)]
public string FirstName { get; set; }
[DataMember(IsRequired=false,Order=1)]
public string LastName { get; set; }
[DataMember(IsRequired = false, Order = 2)]
public bool IsSuccess { get; set; }
}
}
上面的源码就是一个REST设计风格的Web Service,这上面的属性有些是WCF特有的属性,比如像[ServiceContact]代表的是一个服务契约,使用在类,接口之上,这里面其实就是指定服务将要由谁来完成;[OperationContract] 属性使用在方法之前,因为在服务契约里面,有些方法是可以供外部调用([OperationContract] ),而有些方法则不需要考虑给外部调用,所以可以理解为它就是用来区分方法调用的属性。[DataContract]属性主要是用来指定数据契约,一般用在类上面,供服务契约[ServiceContact]里面使用。而[DataMember]其实可以跟[OperationContract] 一样来理解,就是数据契约[DataContract]里面有些成员变量可以供外部获取,有些则不需要。
下面来认识一下REST的属性,[WebGet()]和[WebInvoke()]。这两个属性的不同,主要是后者是包括Method为POST、PUT和DELETE,而Method为GET,就直接用WebGet的方式来表示就可以了,通过上面的代码,我想大家也看到了,WebGet里面没有Method的参数,因为它已经是GET了。UriTemplate这个参数的意思,就是供外部调用的URI结构方式,我想它的好处是使URI更有层次感,更加灵活,当然也更直白。对于像[WebGet(UriTemplate = "/username/{username}/password/{password}")]里面的{username}作用,它是表示那个占位符放的是一个变量,这个变量对应User GetUser(string username, string password)方法里面的形参。
定义好了一个接口之后,下面一步我们就需要实现这个接口里面的方法,定义一个继承于IOperationUser.cs接口的OperationUser.cs类。实现的方法如下:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; using System.IO; using System.Xml.Linq; using System.Web; namespace WcfRest { public class OperationUser : IOperationUser { //User.xml文件的绝对路径 private static string addressPath = @"D:\Project\CDN\TestProject\TestProject\WcfRest\WcfRest\User.xml"; private XElement GetElement() { return XElement.Load(addressPath); } //把流转换为字典的方式,这里面是以"&"来分隔private Dictionary<string, string> ParsePostData(Stream postStream) { StreamReader reader = new StreamReader(postStream); string postData = reader.ReadToEnd(); string[] keyValuePairs = postData.Split('&'); Dictionary<string, string> postDic = new Dictionary<string, string>(); foreach (var item in keyValuePairs) { string[] itemPair = item.Split('='); if (itemPair.Length >= 2) postDic.Add(itemPair[0], itemPair[1]); } return postDic; }//查询用户,如果用户存在的话(username和password),取出firstname和lastnamepublic User GetUser(string username, string password) { XElement doc = GetElement(); var user = from result in doc.Elements() where (string)result.Attribute("username") == username&&(string)result.Attribute("password") == password select new{ firstname = (string)result.Element("firstname"), lastname = (string)result.Element("lastname") }; User getUser = new User(); foreach (var array in user.ToArray()) { getUser.FirstName = array.firstname; getUser.LastName = array.lastname; getUser.IsSuccess = true; } return getUser; }//增加用户,首先判断用户有没有存在(username),如没有,则增加新用户 public User PostUser(Stream postStream) { User user = new User(); try { Dictionary<string, string> postValues = ParsePostData(postStream); XElement doc = GetElement();var chkUser = from result in doc.Elements() where (string)result.Attribute("username") == postValues["username"] select result;if (chkUser.ToArray().Length <= 0) { XElement newPost = new XElement("user", new XAttribute("username", postValues["username"]), new XAttribute("password", postValues["password"]), new XElement("firstname", postValues["firstname"]), new XElement("lastname", postValues["lastname"]) ); doc.Add(newPost); doc.Save(addressPath); user.IsSuccess = true; } else { user.IsSuccess = false; } } catch { user.IsSuccess = false; } return user; }//修改用户信息,首先判断用户有没有存在,如果有的话,则进行修改 public User PutUser(Stream putStream) { User user = new User(); try { Dictionary<string, string> postValues = ParsePostData(putStream); XElement doc = GetElement(); var chkUser = from result in doc.Elements() where (string)result.Attribute("username") == postValues["username"] && (string)result.Attribute("password") == postValues["password"] select result; if (chkUser.ToArray().Length >= 0) { XElement newPost = new XElement("user", new XAttribute("username", postValues["username"]), new XAttribute("password", postValues["password"]), new XElement("firstname", postValues["firstname"]), new XElement("lastname", postValues["lastname"]) ); doc.Add(newPost); doc.Save(addressPath); user.IsSuccess = true; } else { user.IsSuccess = false; } } catch { user.IsSuccess = false; } return user; } //删除用户,以用户名来判断有没有存在此用户,有的话,则删除 public User DeleteUser(string username) { User user = new User(); try { XElement doc = GetElement(); var chkUser = (doc.Elements().Where(e=>(string)e.Attribute("username") == username)).FirstOrDefault(); if(chkUser != null) { chkUser.Remove(); doc.Save(addressPath); user.IsSuccess = true; } else { user.IsSuccess = false; } } catch { user.IsSuccess = false; } return user; } } }
上面的代码基本上就已经实现好我们的实例了,它的功能主要是对User.xml文件进行操作,相当于对用户进行增删改查。还有就是web.config文件里面的内容,这样之后,这个服务就能正常工作。代码如下:
<?xml version="1.0"?> <configuration> <system.web> <compilation debug="true" targetFramework="4.0" /> </system.web> <system.serviceModel> <services> <service name="WcfRest.OperationUser" behaviorConfiguration="ServiceBehaviour" > <endpoint address="" binding="webHttpBinding" contract="WcfRest.IOperationUser" behaviorConfiguration="web"></endpoint> <host> <baseAddresses> <add baseAddress="http://localhost:9762" /> </baseAddresses> </host> </service> </services> <behaviors> <serviceBehaviors> <behavior name="ServiceBehaviour"> <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --> <serviceMetadata httpGetEnabled="true"/> <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> <endpointBehaviors> <behavior name="web"> <webHttp/> </behavior> </endpointBehaviors> </behaviors> <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> </system.serviceModel> <system.webServer> <modules runAllManagedModulesForAllRequests="true"/> </system.webServer> </configuration>
被操作的User.xml文件的内容如下:
<?xml version="1.0" encoding="utf-8"?> <users> <user username="peter" password="password"> <firstname>Peter</firstname> <lastname>Zhang</lastname> </user> <user username="linda" password="password"> <firstname>Linda</firstname> <lastname>Wu</lastname> </user> <user username="john" password="password"> <firstname>John</firstname> <lastname>Mei</lastname> </user> <user username="Test" password="Password"> <firstname>Test1</firstname> <lastname>Test2</lastname> </user> </users>
这样之后,本文的代码基本上就已经实现完毕了。下一步就是供外部调用的部分。因为本文的代码只是简单的入门部分,所以就只简单介绍一下Fiddler工具来测试,关于Fiddler工具的使用,大家可以参考此文的说明:通过Fiddler测试你的 REST WCF服务
这里面我简单附一个POST的操作方法,此方法是我们上文中的方法:
[OperationContract]
[WebInvoke(UriTemplate = "/user", Method = "POST")]
User PostUser(Stream postStream);
底下的RequestBody就是我们上面这个方法里面需要的参数,这里是以流的方式来传递,具体流里面的字符串结构是怎样,这个大家可以自己设计。
本文内容写得比较浅显,如果大家有什么意见的话,欢迎指出!
如果您也想转载本文的话,请指明出处,谢谢合作!