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.