[转]构建基于WCF Restful Service的服务
本文转自:http://www.cnblogs.com/scy251147/p/3566638.html
前言
传统的Asmx服务,由于遵循SOAP协议,所以返回内容以xml方式组织。并且客户端需要添加服务端引用才能使用(虽然看到网络上已经提供了这方面的Dynamic Proxy,但是没有这种方式简便),所以给开发和部署带来了不小的麻烦。并且当服务过多的时候,生成的引用文件会很大,之前项目的一个引用文件光引用代码都有5000多行,全部在一个类中。确实不方便维护。
基于以上几点,就特别研究了一下基于Restful的服务开发,当时手头有两种框架,一个是WCF Restful Service,另一个是Asp.net Web API。由于对WCF比较熟悉一些,所以就选择了前者。
Restful Service及其相关
说到Restful Service,不得不提到其中的Rest这个关键字。它是用于创建分布式超文本媒体的一种架构方式,我们可以通过标准的HTTP(GET,POST,PUT,DELETE)操作来构建基于面向资源的软件架构方式(Resource-Oriented Architecture (ROA))。它是独立于任何技术或者平台的,所以人们经常将符合这种操作规范的服务称为“RESTful services”。因为WCF能够构建符合这种规范的服务,所以我们经常称之为 WCF Restful Services。
由于传统的WCF Service可以使用tcp,net.msmq,http等协议进行数据交换,并且采用了RPC(Remote Procedure Call)的工作方式,客户端需要添加对服务端的引用才能完成。但是WCF Restful Service完全使用Http协议来进行,并且无需添加客户端引用,所以方便很多。
服务端开发一瞥
下面以图书馆的例子来做具体的说明。
打开VS2010,新建一个WCF REST Service Application项目,然后在项目中,添加一个BookService.cs用于处理逻辑操作,再添加一个BookEntity.cs用于提供实体类。
打开Global.asax,可以看到如下代码:
1: void Application_Start(object sender, EventArgs e)
2: {
3: RegisterRoutes();
4: }
5:
6: private void RegisterRoutes()
7: {
8: RouteTable.Routes.Add(new ServiceRoute("BookService", new WebServiceHostFactory(), typeof(BookService)));
9: }
其中RegisterRoutes是设定服务启动的入口点的。
然后是BookEntity实体类的组织方式:
1: public class BookEntity
2: {
3: public int BookID { get; set; }
4:
5: public string BookName { get; set; }
6:
7: public decimal BookPrice { get; set; }
8:
9: public string BookPublish { get; set; }
10: }
这里我就不用多说了,实体类包含图书序号,图书名称,图书价格,出版单位四个属性。
然后就是我们的核心内容:
1: [ServiceContract]
2: [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
3: [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
4: public class BookService
5: {
6: public BookService()
7: {
8: bookList = new List<BookEntity>();
9: BookEntity book = new BookEntity();
10: book.BookID = 1;
11: book.BookName = "大话设计模式";
12: book.BookPrice = (decimal)45.2;
13: book.BookPublish = "中国邮电出版社";
14: bookList.Add(book);
15:
16: BookEntity book1 = new BookEntity();
17: book1.BookID = 2;
18: book1.BookName = "测试用例";
19: book1.BookPrice = (decimal)21.0;
20: book1.BookPublish = "清华大学出版社";
21: bookList.Add(book1);
22:
23: BookEntity book2 = new BookEntity();
24: book2.BookID = 3;
25: book2.BookName = "Rework";
26: book2.BookPrice = (decimal)15.4;
27: book2.BookPublish = "Wrox pulishment";
28: bookList.Add(book2);
29: }
30:
31: private static List<BookEntity> bookList;
32:
33: [WebInvoke(Method = "GET"
34: , ResponseFormat = WebMessageFormat.Json
35: , BodyStyle = WebMessageBodyStyle.Bare //不需要任何修饰,否则生成的json无法解析
36: , UriTemplate = "/?bookID={bookID}")] //只接收string类型,如果是其他类型,需要按照 /?para={parameter}的方式来组织。
37: public BookEntity Get(int bookID)
38: {
39: return bookList.Where(p => p.BookID == bookID).FirstOrDefault();
40: }
41:
42: [WebInvoke(Method = "GET"
43: , ResponseFormat = WebMessageFormat.Json
44: , BodyStyle = WebMessageBodyStyle.Bare
45: , UriTemplate = "/")]
46: public List<BookEntity> GetALL()
47: {
48: return bookList;
49: }
50:
51: [WebInvoke(Method = "POST"
52: , ResponseFormat = WebMessageFormat.Json
53: , BodyStyle = WebMessageBodyStyle.Bare
54: , UriTemplate = "/")]
55: public bool Update(BookEntity book)
56: {
57: BookEntity query = (from p in bookList where p.BookID == book.BookID select p).FirstOrDefault();
58: bookList.Remove(query);
59: bookList.Add(book);
60: return true;
61: }
62:
63: [WebInvoke(Method = "PUT"
64: , ResponseFormat = WebMessageFormat.Json
65: , BodyStyle = WebMessageBodyStyle.Bare
66: , UriTemplate = "/")]
67: public bool Add(BookEntity book)
68: {
69: bookList.Add(book);
70: return true;
71: }
72:
73: [WebInvoke(Method = "DELETE"
74: , ResponseFormat = WebMessageFormat.Json
75: , BodyStyle = WebMessageBodyStyle.Bare
76: , UriTemplate = "/")]
77: public bool Delete(BookEntity book)
78: {
79: BookEntity bookCurrent = (from p in bookList where p.BookID == book.BookID select p).FirstOrDefault();
80: return bookList.Remove(bookCurrent);
81: }
82: }
其中,Method 方法主要是表明可以接受客户端的请求类型,这里有四种:GET,POST,PUT,DELETE,其中GET为请求数据,POST为更新数据,PUT为新增数据,DELETE代表着删除数据。
然后ResponseFormat 则代表着返回的数据组织,如果是Json则表明客户端会接收到Json数据,如果是XML则表明客户端会接收到XML组织的数据。BodyStyle 代表返回数据的包装对象,如果是Bare则表明数据无任何包装,原生数据返回;如果是Wrapped则表明数据会在最外层包装一个当前函数名称加上Result的套。比如对于Delete对象,则会返回 DeleteResult:{******},会造成DataContractJsonSerializer无法进行反序列化。
UriTemplate 主要用于指定操作的URI路径,只要用户输入了合法路径并采用了正确的请求方式,就会触发该函数。
最后说到的就是URI后面跟的参数的问题,由于函数只能接受string类型的,所以如果传入参数是string类型,则可以使用UriTemplate = "{bookID}"的路径,反之,则需要加上/?param1={paramname}的方式,比如我代码中使用的是:UriTemplate = "/?bookID={bookID}"。
当一切都弄好以后,让我们运行一下,访问如下路径,就可以得到结果:
http://localhost:45345/BookService/
得到的结果如下:
[{"BookID":1,"BookName":"大话设计模式","BookPrice":45.2,"BookPublish":"中国邮电出版社"},{"BookID":2,"BookName":"测试用例","BookPrice":21,"BookPublish":"清华大学出版社"},{"BookID":3,"BookName":"Rework","BookPrice":15.4,"BookPublish":"Wrox pulishment"}]
如果访问http://localhost:45345/BookService/?bookID=1,则会得到如下的结果:
{"BookID":1,"BookName":"大话设计模式","BookPrice":45.2,"BookPublish":"中国邮电出版社"}
客户端开发一瞥
初步测试成功后,让我们来进行一下全面的测试。
首先,在项目中,我们新建一个Asp.net WebForm Application,用于做测试工作。
然后,在Default.aspx.cs中,针对GET操作,我们添加如下代码:
1: private void GetBookByID(string id)
2: {
3: WebClient proxy = new WebClient();
4: string serviceURL = string.Empty;
5: DataContractJsonSerializer obj ;
6: if (string.IsNullOrEmpty(id))
7: {
8: serviceURL = string.Format("http://localhost:45345/BookService/");
9: obj = new DataContractJsonSerializer(typeof(List<BookEntity>));
10: }
11: else
12: {
13: serviceURL = string.Format("http://localhost:45345/BookService/?bookID=" + id);
14: obj = new DataContractJsonSerializer(typeof(BookEntity));
15: }
16: byte[] data = proxy.DownloadData(serviceURL);
17: Stream stream = new MemoryStream(data);
18: var result = obj.ReadObject(stream);
19: List<BookEntity> list=new List<BookEntity>();
20: if (result is BookEntity)
21: list.Add(result as BookEntity);
22: else if (result is List<BookEntity>)
23: list = result as List<BookEntity>;
24: GridView1.DataSource = list;
25: GridView1.DataBind();
26: }
在以上代码中,DataContractJsonSerializer 是WCF提供的一个序列化类,用于将对象序列化或者反序列化。
写好之后,我们点击界面按钮,出现了以下的结果:
针对PUT操作,也就是添加操作,我们添加如下代码:
1: BookEntity bookEntity = new BookEntity();
2: bookEntity.BookID = Int32.Parse(txtBookID.Text);
3: bookEntity.BookName = txtBookName.Text;
4: bookEntity.BookPrice = decimal.Parse(txtBookPrice.Text);
5: bookEntity.BookPublish = txtBookPublish.Text;
6:
7: DataContractJsonSerializer obj = new DataContractJsonSerializer(typeof(BookEntity));
8: MemoryStream ms = new MemoryStream();
9: obj.WriteObject(ms, bookEntity);
10: byte[] byteSend = ms.ToArray();
11: ms.Close();
12:
13: string serviceURL = string.Format("http://localhost:45345/BookService");
14:
15: WebClient test = new WebClient();
16: test.Headers.Add("Content-Type", "application/json");
17: test.Headers.Add("ContentLength", byteSend.Length.ToString());
18:
19:
20: byte[] responseData = test.UploadData(serviceURL, "PUT", byteSend);
21:
22: string result = Encoding.GetEncoding("UTF-8").GetString(responseData);
23: lblLog.Text = result;
在做这步的时候,需要注意,test.Headers.Add("Content-Type", "application/json") 和test.Headers.Add("ContentLength", byteSend.Length.ToString())需要添加,否则会造成Http 400 返回的错误。并且,向服务端传递实体的时候,可以通过使用UploadData的方式来进行,如果数据量过大,可以考虑使用异步方式传送。
接下来的POST和DELETE方法和上面类似,我都贴一下:
POST方法:
1: BookEntity bookEntity = new BookEntity();
2: bookEntity.BookID = Int32.Parse(txtBookID.Text);
3: bookEntity.BookName = txtBookName.Text;
4: bookEntity.BookPrice = decimal.Parse(txtBookPrice.Text);
5: bookEntity.BookPublish = txtBookPublish.Text;
6:
7: DataContractJsonSerializer obj = new DataContractJsonSerializer(typeof(BookEntity));
8: MemoryStream ms = new MemoryStream();
9: obj.WriteObject(ms, bookEntity);
10: byte[] byteSend = ms.ToArray();
11: ms.Close();
12:
13: string serviceURL = string.Format("http://localhost:45345/BookService");
14:
15: WebClient test = new WebClient();
16: test.Headers.Add("Content-Type", "application/json");
17: test.Headers.Add("ContentLength", byteSend.Length.ToString());
18:
19: byte[] responseData = test.UploadData(serviceURL, "POST", byteSend);
20:
21: string result = Encoding.GetEncoding("UTF-8").GetString(responseData);
22: lblLog.Text = result;
DELETE方法:
1: BookEntity bookEntity = new BookEntity();
2: bookEntity.BookID = Int32.Parse(txtBookID.Text);
3:
4: DataContractJsonSerializer obj = new DataContractJsonSerializer(typeof(BookEntity));
5: MemoryStream ms = new MemoryStream();
6: obj.WriteObject(ms, bookEntity);
7: byte[] byteSend = ms.ToArray();
8: ms.Close();
9:
10: string serviceURL = string.Format("http://localhost:45345/BookService");
11:
12: WebClient test = new WebClient();
13: test.Headers.Add("Content-Type", "application/json");
14: test.Headers.Add("ContentLength", byteSend.Length.ToString());
15:
16:
17: byte[] responseData = test.UploadData(serviceURL, "DELETE", byteSend);
18:
19: string result = Encoding.GetEncoding("UTF-8").GetString(responseData);
20: lblLog.Text = result;
最后得到的效果图如下:
(新增记录)
(更新记录)
(删除记录)
成文仓促,难免有误,还请指出,在此谢过。
源代码下载
点击下载源代码
Edit:基于本方法构建的Android服务已经在使用中。后续继续跟进各种使用信息。
在StackOverFlow问答如下:点击这里查看
看评论中提到了IContract问题,由于这是Restful Service,不是基于RPC模式,所以没必要使用的。不过用上去也没错。
[转]构建基于WCF Restful Service的服务相关推荐
- 在Asp.net应用程序中构建基于WCF Web.Api的服务
WCF Web API Preview 5 发布了,你可以官方网站下载或通过Nuget安装它. 下面让我们在Asp.net applicatoin中来实现一个非常简单的web api service. ...
- 报表服务扩展:基于WCF技术的报表服务扩展
这项技术被我鼓吹了很久,今天终于有机会拿出来展示一下. 为什么要进行扩展呢?扩展的目的是为了和自己现有的系统整合在一起.比如现有系统已经很复杂,能处理很多业务,设计系统的开发人员没有充分考虑到技术的变 ...
- macos big sur安装php扩展_用PHP构建基于swoole扩展的socket服务(附PHP扩展安装步骤)...
最近公司的一项目中,需要用PHP搭建一个socket服务. 本来PHP是不适合做服务的,因为和第三方合作,需要采用高效而稳定的TCP协议进行数据通信.经过多次尝试,最终选择了开源的PHP扩展:swoo ...
- 在IIS8.5的环境下配置WCF的Restful Service
今天在客户的环境中(Windows Server 2012 R2 + IIS 8.5)搭建Call WCF Restful Service的功能,发现了几个环境配置的问题,记录如下: 1):此环境先安 ...
- 基于java Restful API 的Sdnote服务端开发笔记
前奏 欢迎访问我的博客:Lunatic 似乎有大半个月没有啥动静,这段时间一直忙大学生计算机设计大赛,被老师说动参加这项比赛.动心一方面是因为可以有动手的机会,另一方面是对未来阶段有些许帮助. 回来后 ...
- 框架:SpringBoot构建Restful service完成Get和Post请求
SpringBoot构建Restful service完成Get和Post请求 一个基本的RESTful service最经常向外提供的请求Method就是Get和Post. 在Get中,常用的都会 ...
- 构建Spring Boot RESTful服务+ Spring Boot执行器
总览 什么是REST? REST(代表状态转移)是Web构建的体系结构样式,已成为用于Web应用程序的标准软件设计模式 . 代表性国家转移一词最早由REST的发起人,HTTP规范的主要作者之一Roy ...
- WCF技术剖析之三:如何进行基于非HTTP的IIS服务寄宿
原文:[原创]WCF技术剖析之三:如何进行基于非HTTP的IIS服务寄宿 在上面一篇文章中,我们对不同版本的IIS,以及ASP.NET得的实现机制进行了详细而深入的分析.在介绍IIS7.0的时候,我们 ...
- 构建基于Spring Cloud向Service Mesh框架迁移的解决方案及思路
作为新一代微服务架构体系,Service Mesh 技术有效地解决了 Spring Cloud 微服务架构和服务治理过程中的痛点问题,一经推出便引起了很大的反响.近一年来,伴随着云原生的热火朝天,Se ...
最新文章
- 【VS实践】代码调试与错误处理
- 代替国足踢决赛?马宁当选卡日大战第四官员
- Spring JDBC-NamedParameterJdbcTemplate模板类
- If语句:你们到底想把我放到哪儿?
- pythonos模块使用方法_python os模块使用方法
- 研发协同平台持续集成Jenkins作业设计演进
- mappedbytebuffer_Java NIO Buffer【MappedByteBuffer】概述与FileChannel的联系
- 独立站大卖家都在用的运营技巧?
- nginx配置注意事项1
- 省选专练HAOI2015树上操作
- phpMyAdmin源码配置过程
- 马哥python培训视频
- towe/ jira / tapd / 禅道 协作平台对比
- 【微信公众号】2、申请微信公众平台接口测试帐号
- IP种子眼中的《延禧攻略》流落何处?
- 计算机上哪个键可以按出符号,键盘符号怎么打出来_各种符号在键盘上怎么打出来-win7之家...
- java 删除本地图片_java如何删除
- 祛眼袋术前注意细节有哪些
- 苹果系统安装虚拟机 Mac如何安装虚拟机教程
- 英语总结系列(十六):这个四月真不错