構建WCF RESTful service示例

iteye_4515發表於2011-12-01
今天來分享一下如何建立一個關於建立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
{
    /// <summary>
    /// The RESTful web service that used to handle image basic functions.
    /// </summary>
    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall, Namespace="PictureService")]
    public class PictureRESTService
    {
        /// <summary>
        /// Images search service.
        /// </summary>
        /// <param name="image"></param>
        /// <returns></returns>
        [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;
        }

        /// <summary>
        /// Image adding service.
        /// </summary>
        /// <param name="message"></param>
        /// <param name="image"></param>
        [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);
        }

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

        /// <summary>
        /// Get specify image by image's Name property.
        /// </summary>
        /// <param name="image"></param>
        /// <returns></returns>
        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;
        }

        /// <summary>
        /// Get all images' physics url string variable.
        /// </summary>
        /// <returns></returns>
        public List<string> GetAllImages()
        {
            List<string> list = new List<string>();
            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;
        }

        /// <summary>
        /// Add XElement to xml file to record new images.
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="physicsUrl"></param>
        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的檔案格式是這樣的

<?xml version="1.0" encoding="utf-8"?>
<Root>
  <Image open="1">
    <Name>a9ba816042594d108c6e597dbfbcd784.jpg</Name>
    <PhysicsUrl>D:\Program1\Sample-code\WCFPhotoRatingSystem\Visual Studio 2010\CSWCFPhotoRatingSystem\CSWCFPhotoRatingSystem\Image\a9ba816042594d108c6e597dbfbcd784.jpg</PhysicsUrl>
  </Image>
</Root>


同時我們需要在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<PictureRESTService>("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頁面。

        /// <summary>
        /// Upload button click event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        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

    /// <summary>
    /// The page is used to show the images and zoom in effect.
    /// </summary>
    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();
            }
        }

        /// <summary>
        /// Get images from PictureRESTService.
        /// </summary>
        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("<li>");
                    builder.Append("<img src=\"");
                    builder.Append(String.Concat(serverUrl, fileName));
                    builder.Append("\"  alt=\"");
                    builder.Append(fileName);
                    builder.Append("\" onclick=\"hidediv('");
                    builder.Append(String.Concat(serverUrl, fileName));
                    builder.Append("');\" />");
                    builder.Append("</li>");
                }
                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.

相關文章