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.