WCF Restful Service的服务
2014-02-25 14:31
399 查看
构建基于WCFRestfulService的服务
前言传统的Asmx服务,由于遵循SOAP协议,所以返回内容以xml方式组织。并且客户端需要添加服务端引用才能使用(虽然看到网络上已经提供了这方面的DynamicProxy,但是没有这种方式简便),所以给开发和部署带来了不小的麻烦。并且当服务过多的时候,生成的引用文件会很大,之前项目的一个引用文件光引用代码都有5000多行,全部在一个类中。确实不方便维护。
基于以上几点,就特别研究了一下基于Restful的服务开发,当时手头有两种框架,一个是WCFRestfulService,另一个是Asp.netWebAPI。由于对WCF比较熟悉一些,所以就选择了前者。
RestfulService及其相关
说到RestfulService,不得不提到其中的Rest这个关键字。它是用于创建分布式超文本媒体的一种架构方式,我们可以通过标准的HTTP(GET,POST,PUT,DELETE)操作来构建基于面向资源的软件架构方式(Resource-OrientedArchitecture(ROA))。它是独立于任何技术或者平台的,所以人们经常将符合这种操作规范的服务称为“RESTfulservices”。因为WCF能够构建符合这种规范的服务,所以我们经常称之为WCFRestfulServices。
由于传统的WCFService可以使用tcp,net.msmq,http等协议进行数据交换,并且采用了RPC(RemoteProcedureCall)的工作方式,客户端需要添加对服务端的引用才能完成。但是WCFRestfulService完全使用Http协议来进行,并且无需添加客户端引用,所以方便很多。
服务端开发一瞥
下面以图书馆的例子来做具体的说明。
打开VS2010,新建一个WCFRESTServiceApplication项目,然后在项目中,添加一个BookService.cs用于处理逻辑操作,再添加一个BookEntity.cs用于提供实体类。
打开Global.asax,可以看到如下代码:
voidApplication_Start(objectsender,EventArgse)
[code]{
RegisterRoutes();
}
privatevoidRegisterRoutes()
{
RouteTable.Routes.Add(newServiceRoute("BookService",newWebServiceHostFactory(),typeof(BookService)));
}
[/code]
其中RegisterRoutes是设定服务启动的入口点的。
然后是BookEntity实体类的组织方式:
publicclassBookEntity
[code]{
publicintBookID{get;set;}
publicstringBookName{get;set;}
publicdecimalBookPrice{get;set;}
publicstringBookPublish{get;set;}
}
[/code]
这里我就不用多说了,实体类包含图书序号,图书名称,图书价格,出版单位四个属性。
然后就是我们的核心内容:
[ServiceContract]
[code][AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
publicclassBookService
{
publicBookService()
{
bookList=newList<BookEntity>();
BookEntitybook=newBookEntity();
book.BookID=1;
book.BookName="大话设计模式";
book.BookPrice=(decimal)45.2;
book.BookPublish="中国邮电出版社";
bookList.Add(book);
BookEntitybook1=newBookEntity();
book1.BookID=2;
book1.BookName="测试用例";
book1.BookPrice=(decimal)21.0;
book1.BookPublish="清华大学出版社";
bookList.Add(book1);
BookEntitybook2=newBookEntity();
book2.BookID=3;
book2.BookName="Rework";
book2.BookPrice=(decimal)15.4;
book2.BookPublish="Wroxpulishment";
bookList.Add(book2);
}
privatestaticList<BookEntity>bookList;
[WebInvoke(Method="GET"
,ResponseFormat=WebMessageFormat.Json
,BodyStyle=WebMessageBodyStyle.Bare//不需要任何修饰,否则生成的json无法解析
,UriTemplate="/?bookID={bookID}")]//只接收string类型,如果是其他类型,需要按照/?para={parameter}的方式来组织。
publicBookEntityGet(intbookID)
{
returnbookList.Where(p=>p.BookID==bookID).FirstOrDefault();
}
[WebInvoke(Method="GET"
,ResponseFormat=WebMessageFormat.Json
,BodyStyle=WebMessageBodyStyle.Bare
,UriTemplate="/")]
publicList<BookEntity>GetALL()
{
returnbookList;
}
[WebInvoke(Method="POST"
,ResponseFormat=WebMessageFormat.Json
,BodyStyle=WebMessageBodyStyle.Bare
,UriTemplate="/")]
publicboolUpdate(BookEntitybook)
{
BookEntityquery=(frompinbookListwherep.BookID==book.BookIDselectp).FirstOrDefault();
bookList.Remove(query);
bookList.Add(book);
returntrue;
}
[WebInvoke(Method="PUT"
,ResponseFormat=WebMessageFormat.Json
,BodyStyle=WebMessageBodyStyle.Bare
,UriTemplate="/")]
publicboolAdd(BookEntitybook)
{
bookList.Add(book);
returntrue;
}
[WebInvoke(Method="DELETE"
,ResponseFormat=WebMessageFormat.Json
,BodyStyle=WebMessageBodyStyle.Bare
,UriTemplate="/")]
publicboolDelete(BookEntitybook)
{
BookEntitybookCurrent=(frompinbookListwherep.BookID==book.BookIDselectp).FirstOrDefault();
returnbookList.Remove(bookCurrent);
}
}
[/code]
其中,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}"。
当一切都弄好以后,让我们运行一下,访问如下路径,就可以得到结果:
得到的结果如下:
[{"BookID":1,"BookName":"大话设计模式","BookPrice":45.2,"BookPublish":"中国邮电出版社"},{"BookID":2,"BookName":"测试用例","BookPrice":21,"BookPublish":"清华大学出版社"},{"BookID":3,"BookName":"Rework","BookPrice":15.4,"BookPublish":"Wroxpulishment"}]
如果访问http://localhost:45345/BookService/?bookID=1,则会得到如下的结果:
{"BookID":1,"BookName":"大话设计模式","BookPrice":45.2,"BookPublish":"中国邮电出版社"}
客户端开发一瞥
初步测试成功后,让我们来进行一下全面的测试。
首先,在项目中,我们新建一个Asp.netWebFormApplication,用于做测试工作。
然后,在Default.aspx.cs中,针对GET操作,我们添加如下代码:
privatevoidGetBookByID(stringid)
[code]{
WebClientproxy=newWebClient();
stringserviceURL=string.Empty;
DataContractJsonSerializerobj;
if(string.IsNullOrEmpty(id))
{
serviceURL=string.Format("http://localhost:45345/BookService/");
obj=newDataContractJsonSerializer(typeof(List<BookEntity>));
}
else
{
serviceURL=string.Format("http://localhost:45345/BookService/?bookID="+id);
obj=newDataContractJsonSerializer(typeof(BookEntity));
}
byte[]data=proxy.DownloadData(serviceURL);
Streamstream=newMemoryStream(data);
varresult=obj.ReadObject(stream);
List<BookEntity>list=newList<BookEntity>();
if(resultisBookEntity)
list.Add(resultasBookEntity);
elseif(resultisList<BookEntity>)
list=resultasList<BookEntity>;
GridView1.DataSource=list;
GridView1.DataBind();
}
[/code]
在以上代码中,DataContractJsonSerializer是WCF提供的一个序列化类,用于将对象序列化或者反序列化。
写好之后,我们点击界面按钮,出现了以下的结果:
针对PUT操作,也就是添加操作,我们添加如下代码:
BookEntitybookEntity=newBookEntity();
[code]bookEntity.BookID=Int32.Parse(txtBookID.Text);
bookEntity.BookName=txtBookName.Text;
bookEntity.BookPrice=decimal.Parse(txtBookPrice.Text);
bookEntity.BookPublish=txtBookPublish.Text;
DataContractJsonSerializerobj=newDataContractJsonSerializer(typeof(BookEntity));
MemoryStreamms=newMemoryStream();
obj.WriteObject(ms,bookEntity);
byte[]byteSend=ms.ToArray();
ms.Close();
stringserviceURL=string.Format("http://localhost:45345/BookService");
WebClienttest=newWebClient();
test.Headers.Add("Content-Type","application/json");
test.Headers.Add("ContentLength",byteSend.Length.ToString());
byte[]responseData=test.UploadData(serviceURL,"PUT",byteSend);
stringresult=Encoding.GetEncoding("UTF-8").GetString(responseData);
lblLog.Text=result;
[/code]
在做这步的时候,需要注意,test.Headers.Add("Content-Type","application/json")和test.Headers.Add("ContentLength",byteSend.Length.ToString())需要添加,否则会造成Http400返回的错误。并且,向服务端传递实体的时候,可以通过使用UploadData的方式来进行,如果数据量过大,可以考虑使用异步方式传送。
接下来的POST和DELETE方法和上面类似,我都贴一下:
POST方法:
BookEntitybookEntity=newBookEntity();
[code]bookEntity.BookID=Int32.Parse(txtBookID.Text);
bookEntity.BookName=txtBookName.Text;
bookEntity.BookPrice=decimal.Parse(txtBookPrice.Text);
bookEntity.BookPublish=txtBookPublish.Text;
DataContractJsonSerializerobj=newDataContractJsonSerializer(typeof(BookEntity));
MemoryStreamms=newMemoryStream();
obj.WriteObject(ms,bookEntity);
byte[]byteSend=ms.ToArray();
ms.Close();
stringserviceURL=string.Format("http://localhost:45345/BookService");
WebClienttest=newWebClient();
test.Headers.Add("Content-Type","application/json");
test.Headers.Add("ContentLength",byteSend.Length.ToString());
byte[]responseData=test.UploadData(serviceURL,"POST",byteSend);
stringresult=Encoding.GetEncoding("UTF-8").GetString(responseData);
lblLog.Text=result;
[/code]
DELETE方法:
BookEntitybookEntity=newBookEntity();
[code]bookEntity.BookID=Int32.Parse(txtBookID.Text);
DataContractJsonSerializerobj=newDataContractJsonSerializer(typeof(BookEntity));
MemoryStreamms=newMemoryStream();
obj.WriteObject(ms,bookEntity);
byte[]byteSend=ms.ToArray();
ms.Close();
stringserviceURL=string.Format("http://localhost:45345/BookService");
WebClienttest=newWebClient();
test.Headers.Add("Content-Type","application/json");
test.Headers.Add("ContentLength",byteSend.Length.ToString());
byte[]responseData=test.UploadData(serviceURL,"DELETE",byteSend);
stringresult=Encoding.GetEncoding("UTF-8").GetString(responseData);
lblLog.Text=result;
[/code]
最后得到的效果图如下:
(新增记录)
(更新记录)
(删除记录)
成文仓促,难免有误,还请指出,在此谢过。
源代码下载
Edit:基于本方法构建的Android服务已经在使用中。后续继续跟进各种使用信息。
在StackOverFlow问答如下:
看评论中提到了IContract问题,由于这是RestfulService,不是基于RPC模式,所以没必要使用的。不过用上去也没错。
C#编程总结(十三)数据压缩
Postedon2014-02-2514:31C#编程总结(十三)数据压缩
在进行文件存储或者数据传输时,为了节省空间流量,需要对数据或文件进行压缩。在这里我们讲述通过C#实现数据压缩。一、GZipStream压缩
微软提供用于压缩和解压缩流的方法。此类表示GZip数据格式,它使用无损压缩和解压缩文件的行业标准算法。这种格式包括一个检测数据损坏的循环冗余校验值。GZip数据格式使用的算法与DeflateStream类的算法相同,但它可以扩展以使用其他压缩格式。这种格式可以通过不涉及专利使用权的方式轻松实现。
可以使用许多常见的压缩工具对写入到扩展名为.gz的文件的压缩GZipStream对象进行解压缩;但是,此类原本并不提供用于向.zip存档中添加文件或从.zip存档中提取文件的功能。
DeflateStream和GZipStream中的压缩功能作为流公开。由于数据是以逐字节的方式读取的,因此无法通过进行多次传递来确定压缩整个文件或大型数据块的最佳方法。对于未压缩的数据源,最好使用DeflateStream和GZipStream类。如果源数据已压缩,则使用这些类时实际上可能会增加流的大小。
具体实现源码
1、压缩字节数组
///<summary>
///压缩字节数组
///</summary>
///<paramname="str"></param>
publicstaticbyte[]Compress(byte[]inputBytes)
{
using(MemoryStreamoutStream=newMemoryStream())
{
using(GZipStreamzipStream=newGZipStream(outStream,CompressionMode.Compress,true))
{
zipStream.Write(inputBytes,0,inputBytes.Length);
zipStream.Close();//很重要,必须关闭,否则无法正确解压
returnoutStream.ToArray();
}
}
}
///<summary>
///解压缩字节数组
///</summary>
///<paramname="str"></param>
publicstaticbyte[]Decompress(byte[]inputBytes)
{
using(MemoryStreaminputStream=newMemoryStream(inputBytes))
{
using(MemoryStreamoutStream=newMemoryStream())
{
using(GZipStreamzipStream=newGZipStream(inputStream,CompressionMode.Decompress))
{
zipStream.CopyTo(outStream);
zipStream.Close();
returnoutStream.ToArray();
}
}
}
}
2、压缩字符串
在压缩字节的基础扩展而来,注意字符转换,保证不出现乱码。具体原理,这里不再介绍,可见:C#编程总结(十)字符转码
///<summary>
///压缩字符串
///</summary>
///<paramname="input"></param>
///<returns></returns>
publicstaticstringCompress(stringinput)
{
byte[]inputBytes=Encoding.Default.GetBytes(input);
byte[]result=Compress(inputBytes);
returnConvert.ToBase64String(result);
}
///<summary>
///解压缩字符串
///</summary>
///<paramname="input"></param>
///<returns></returns>
publicstaticstringDecompress(stringinput)
{
byte[]inputBytes=Convert.FromBase64String(input);
byte[]depressBytes=Decompress(inputBytes);
returnEncoding.Default.GetString(depressBytes);
}
3、压缩文件
如果你试图自己做一个压缩工具,相比这个方法很管用///<summary>
///压缩目录
///</summary>
///<paramname="dir"></param>
publicstaticvoidCompress(DirectoryInfodir)
{
foreach(FileInfofileToCompressindir.GetFiles())
{
Compress(fileToCompress);
}
}
///<summary>
///解压缩目录
///</summary>
///<paramname="dir"></param>
publicstaticvoidDecompress(DirectoryInfodir)
{
foreach(FileInfofileToCompressindir.GetFiles())
{
Decompress(fileToCompress);
}
}
///<summary>
///压缩文件
///</summary>
///<paramname="fileToCompress"></param>
publicstaticvoidCompress(FileInfofileToCompress)
{
using(FileStreamoriginalFileStream=fileToCompress.OpenRead())
{
if((File.GetAttributes(fileToCompress.FullName)&FileAttributes.Hidden)!=FileAttributes.Hidden&fileToCompress.Extension!=".gz")
{
using(FileStreamcompressedFileStream=File.Create(fileToCompress.FullName+".gz"))
{
using(GZipStreamcompressionStream=newGZipStream(compressedFileStream,CompressionMode.Compress))
{
originalFileStream.CopyTo(compressionStream);
}
}
}
}
}
///<summary>
///解压缩文件
///</summary>
///<paramname="fileToDecompress"></param>
publicstaticvoidDecompress(FileInfofileToDecompress)
{
using(FileStreamoriginalFileStream=fileToDecompress.OpenRead())
{
stringcurrentFileName=fileToDecompress.FullName;
stringnewFileName=currentFileName.Remove(currentFileName.Length-fileToDecompress.Extension.Length);
using(FileStreamdecompressedFileStream=File.Create(newFileName))
{
using(GZipStreamdecompressionStream=newGZipStream(originalFileStream,CompressionMode.Decompress))
{
decompressionStream.CopyTo(decompressedFileStream);
}
}
}
}
二、开源组件ICSharpCode.SharpZipLib进行压缩
ICSharpCode.SharpZipLib,开源组件,支持Zip,GZip,BZip2,Tar等其压缩效率及压缩比比微软自带的要好。并提供了源码,开源对其算法进行研究、改进。具体可见:
这里提供简单的一种实现以供参考,其他算法比较类似,不再赘述。
1、使用BZip2压缩字符串
///<summary>
///压缩
///</summary>
///<paramname="input"></param>
///<returns></returns>
publicstaticstringCompress(stringinput)
{
stringresult=string.Empty;
byte[]buffer=Encoding.UTF8.GetBytes(input);
using(MemoryStreamoutputStream=newMemoryStream())
{
using(BZip2OutputStreamzipStream=newBZip2OutputStream(outputStream))
{
zipStream.Write(buffer,0,buffer.Length);
zipStream.Close();
}
returnConvert.ToBase64String(outputStream.ToArray());
}
}
///<summary>
///解压缩
///</summary>
///<paramname="input"></param>
///<returns></returns>
publicstaticstringDecompress(stringinput)
{
stringresult=string.Empty;
byte[]buffer=Convert.FromBase64String(input);
using(StreaminputStream=newMemoryStream(buffer))
{
BZip2InputStreamzipStream=newBZip2InputStream(inputStream);
using(StreamReaderreader=newStreamReader(zipStream,Encoding.UTF8))
{
//输出
result=reader.ReadToEnd();
}
}
returnresult;
}
三、Demo下载地址
四、后续
如有其他更好的压缩方法,请指出。后续会更新至此。相关文章推荐
- 单例模式getInstance的有效实现
- Hadoop默认端口说明
- Magic Index 求解
- 通过反射把list<T>转换成datatable,kill进程,事务传入一个Lst<String>多条sql语句
- 快速记忆法-节点,A~Z人名
- void的运用
- Android adb命令总结
- dos下进入mysql环境
- 各种平台的useragent和点击置顶图片功能
- TextView在xml里配置onclick事件
- awk语法解析及实例
- List使用linq的OrderBy方法排序,并按照两个字段排序的写法
- 堆排序
- 【分享】20000篇中文事件标注数据(样例)
- jvm执行流程(static代码块和初始化快和父类子类执行过程)
- Hadoop配置: core-site.xml hdfs-site.xml mapred-site.xml
- java spring mvc+mysql 整合写的一个简单的登录
- 求二元查找树的镜像 C++实现
- baidu seo
- Ubuntu 12.10 安装Tomcat 7