构建WCF RESTful service示例

今天来分享一下如何建立一个关于建立WCF RESTful service的示例, REST(Representational State Transfer)是一种轻量级的Web Service架构,操作比传统的基于Operation操作的WCF service和Web service更为简洁实用,可以完全通过HTTP协议实现,还可以通过缓存来提高相应速度,性能效率和易用性上都要优于SOAP协议,所以比较推荐使用RESTful service取代SOAP service。

REST架构遵循了CRUD原则,即四种行为:Create, Read, Update, Delete. 通过这四种行为完成一般的操作和处理,并且可以通过结合这些操作完成复杂的service结构。REST架构让我们可以更加深入的了解HTTP协议, 针对于协议的编程,更有利于开发出伸缩性强的应用。

REST将网络上的所有事物都抽象为资源,并通过连接器接口对资源进行CRUD的操作。

好,闲话少说,通过一个例子来说明RESTful service的构建和调用方式。

这个例子是关于图片的一个例子,说明我们怎样通过service来上传图片,和浏览图片,并且通过service将我们的local资源可以上载的网络中,节省空间。当然根据你的需要,可以把代码改变为自己的应用程序的逻辑代码。

为了证明这个service的可用性 我们需要建立两个project来完成这个项目,一个为Server,另一个为Client,Client端负责通过service与Service端交互,所以service应该部署在Server端的Web application中。

[本示例完整源码下载(0分)] http://download.csdn.net/detail/aa466564931/3867365

创建PictureRESTService.cs类 并且通过下载Web API来创建这个service, 这里的Web API是微软开发的一套帮助我们创建和使用RESTful service的工具,大家可以在这里下载,并且引用在项目中,注意这些API有相互引用的地方所以建议大家在引用时候全部加入引用。

[最新的版本 WCF Web API Preview 5] http://wcf.codeplex.com/releases/view/73399

引用例子: using Microsoft.ApplicationServer.Http;

PictureRESTService.cs

namespace CSWCFPhotoRatingSystem
{
    /// 
    /// The RESTful web service that used to handle image basic functions.
    /// 
    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall, Namespace="PictureService")]
    public class PictureRESTService
    {
        /// 
        /// Images search service.
        /// 
        /// 
        /// 
        [WebGet(UriTemplate="/{image}")]
        public HttpResponseMessage GetImages(string image)
        {
            var response = new HttpResponseMessage();
            Stream memory = new MemoryStream();
            memory = GetImageByName(image);
            memory.Position = 0;
            response.Content = new StreamContent(memory);
            response.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
            return response;
        }

        /// 
        /// Image adding service.
        /// 
        /// 
        /// 
        [WebInvoke(UriTemplate = "/{image}", Method = "POST")]
        public void AddImages(HttpRequestMessage message,string image)
        {
            var directory = System.Web.HttpContext.Current.Server.MapPath("~/Images");
            var file = Path.Combine(directory, image);
            Stream stream = message.Content.ContentReadStream;
            FileStream fileStream = File.Create(System.Web.HttpContext.Current.Server.MapPath("~/Image/") + image);
            stream.CopyTo(fileStream);
            stream.Close();
            fileStream.Close();
            this.XmlFileAppend(image, System.Web.HttpContext.Current.Server.MapPath("~/Image/") + image);
        }

        /// 
        /// Images information searching service.
        /// 
        /// 
        [WebGet(UriTemplate="",ResponseFormat=WebMessageFormat.Json)]
        public List GetImageList()
        {
            List list = this.GetAllImages();
            if (list != null && list.Count != 0)
            {
                return list;
            }
            else
            {
                return null;
            }
        }

        /// 
        /// Get specify image by image's Name property.
        /// 
        /// 
        /// 
        public Stream GetImageByName(string image)
        {
            XDocument document = XDocument.Load(System.Web.HttpContext.Current.Server.MapPath("~/App_Data/Photo.xml"));
            var nodeList = from node in document.Descendants("Image")
                           where node.Attribute("open").Value.ToString().Equals("1")
                           select node;
            foreach (var node in nodeList)
            {
                if (node.Element("Name").Value.Equals(image))
                {
                    FileStream fileStream = null;
                    string physicUrl = node.Element("PhysicsUrl").Value;
                    fileStream = File.Open(physicUrl, FileMode.Open);
                    MemoryStream ms = new MemoryStream();
                    fileStream.CopyTo(ms);
                    return ms;    
                }
            }
            return null;
        }

        /// 
        /// Get all images' physics url string variable.
        /// 
        /// 
        public List GetAllImages()
        {
            List list = new List();
            XDocument document = XDocument.Load(System.Web.HttpContext.Current.Server.MapPath("~/App_Data/Photo.xml"));
            var nodeList = from node in document.Descendants("Image")
                           where node.Attribute("open").Value.ToString().Equals("1")
                           select node;
            foreach (var node in nodeList)
            {
                list.Add(node.Element("PhysicsUrl").Value);
            }
            return list;
        }

        /// 
        /// Add XElement to xml file to record new images.
        /// 
        /// 
        /// 
        protected void XmlFileAppend(string fileName, string physicsUrl)
        {
            string xmlPath = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/Photo.xml");
            XDocument document = XDocument.Load(xmlPath);
            XElement element = new XElement("Image", new XElement("Name", fileName), new XElement("PhysicsUrl", physicsUrl));
            element.Add(new XAttribute("open", "1"));
            document.Element("Root").Add(element);
            document.Save(xmlPath);
        }
    }


这里通过Get方式获取image 和所有image信息列表,通过Post方式上传image到server,并且把路径和名称等信息通过XDocument存储在xml文件中,xml的文件格式是这样的



  
    a9ba816042594d108c6e597dbfbcd784.jpg
    D:\Program1\Sample-code\WCFPhotoRatingSystem\Visual Studio 2010\CSWCFPhotoRatingSystem\CSWCFPhotoRatingSystem\Image\a9ba816042594d108c6e597dbfbcd784.jpg
  


同时我们需要在Global.ascx文件的Application_Start方法中加入Url Routing的内容,以及上传的Stream的大小调整,如果不做调整默认为65K左右。目前我测试的尺寸可以达到3Mb的图片.

Global.asax.cs

        protected void Application_Start(object sender, EventArgs e)
        {
            RouteTable.Routes.SetDefaultHttpConfiguration(new WebApiConfiguration()
            {
                MaxReceivedMessageSize = int.MaxValue,
                MaxBufferSize = int.MaxValue,
                EnableTestClient = true
            });
            
            RouteTable.Routes.MapServiceRoute("images");
        }


部署完成后你可以根据localhost+port+images+imageName直接访问你的图片,前提是你必须要在image中已有图片并且xml文件有你的注册信息。

url示例:http://localhost:20242/images/a9ba816042594d108c6e597dbfbcd784.jpg

接下我们开始部署客户端的代码,需要两个页面Default和UploadImage,Default用于显示效果,Upload用于上传。这里我们在Upadload前台页面穿件有个FileUpload控件和一个按钮(Button),fileUpload id为fupImageUpload。 并且附加一个Default页面链接 方便你上传完成后直接redirect到Default页面。

        /// 
        /// Upload button click event.
        /// 
        /// 
        /// 
        protected void btnUoload_Click(object sender, EventArgs e)
        {
            if (!fupImageUpload.HasFile)
            {
                Response.Write("Please choose a image.");
            }
            else
            {
                string fileName = fupImageUpload.FileName;
                string fileExtension = Path.GetExtension(fileName).ToLower();
                if (fileExtension.Equals(".jpg") || fileExtension.Equals(".gif") || fileExtension.Equals(".bmp"))
                {
                    string randomName = Guid.NewGuid().ToString().Replace("-", "");
                    string newFileName = String.Concat(randomName, fileExtension);
                    string url = string.Format(serverUrl, newFileName);
                    WebRequest request = WebRequest.Create(url);
                    request.Method = "POST";
                    Stream stream = request.GetRequestStream();
                    (fupImageUpload.PostedFile.InputStream).CopyTo(stream);
                    request.GetResponse();
                    Response.Write(newFileName+" upload success.");
                }
                else
                {
                    Response.Write("The file extension name is incorrect, we only support for these images type now(jpg, gif, bmp). ");
                }
            }
        }


这里通过WebRequest 方式调用RESTful service的AddImages方法,特别指定了端口,大家可能需要改变为你的server程序开启的端口。其中serverUrl为常量,const string serverUrl = "http://localhost:48508/images/{0}"; 你当然可以把它放在config文件中,便于修改。

接下来是Default页面,这块是我用JavaScript给出了一个Ajax SlideShow的效果,并且支持Zoom in(放大),这块不是这个示例的重点,大家可以下载源码看看效果。

其中后台是调用Service的GetImageList的方式获取所有图片的信息,再通过JavaScript显示出来,为了达到更好的展示效果,最好上传两张或两张以上的图片.

Default.aspx.cs

    /// 
    /// The page is used to show the images and zoom in effect.
    /// 
    public partial class Default : System.Web.UI.Page
    {
        public string ulConstruct = string.Empty;
        const string serverUrl = "http://localhost:48508/images/";
        WebRequest request = WebRequest.Create(serverUrl);
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                this.GetResult();
            }
        }

        /// 
        /// Get images from PictureRESTService.
        /// 
        public void GetResult()
        {
            WebResponse response = request.GetResponse();
            HttpWebResponse webResponse = response as HttpWebResponse;
            XDocument document;
            if (webResponse.StatusCode.Equals(HttpStatusCode.OK))
            {
                using (StreamReader reader = new StreamReader(webResponse.GetResponseStream(),Encoding.GetEncoding("utf-8")))
                {
                     document = XDocument.Load(reader);
                }
                var nodeList = from node in document.Descendants("string")
                               select node;
                StringBuilder builder = new StringBuilder();
                int i= 0;
                foreach (var node in nodeList)
                {
                    string fileName = node.Value.Substring(node.Value.ToString().LastIndexOf("\\") + 1);
                    i++;
                    builder.Append("
  • "); builder.Append("\"");"); builder.Append("
  • "); } if (i.Equals(0)) { Response.Write("Please upload some images for viewing."); lbMessage.Text = ""; } else { ulConstruct = builder.ToString(); lbMessage.Text = "Big format:"; } } else { Response.Write("Web request failed."); } } }


    好了 这里有一个ulConstruct的string变量,用于显示HTML的code在Default.aspx信息,大家可以看看下载文件中的HTML页面的详细信息,这里就不列举这些前台页面的代码了。

    如果一切顺利的话,就可以debug了。

    如果在开发中看到远程服务错误或者是400, 500的错误的话,关注下你的配置文件和代码是否正确,get方式的话你可以直接将链接放在浏览器中进行测试,判断问题是出现在service上还是配置上。

    Ok.

    你可能感兴趣的:(WCF,service,wcf,image,string,stream,upload)