寫在前面
上篇文章介紹如何將wcf項目,修改成restful風格的接口,並在上面提供了查詢的功能,上篇文章中也感謝園友在評論中的提的建議,自己也思考了下,確實是那個道理。在urltemplate中,定義的url確實不規范,雖然能實現功能,但是缺少點專業性。rest風格的請求,是通過post,delete,get,put等請求方法來區別的,而不是通過在url中取不同的名字來進行區別。這里再次感謝@~Js園友提醒。
在這篇文章中將最新的代碼貼出來,方便查看。
系列文章
Restful風格wcf調用 (其中代碼中的uritemplate定義不規范,建議參考本文)
代碼示例
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; using System.Web.Script.Services; namespace Wolfy.WCFRestfuleDemo { // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together. [ServiceContract] public interface IUserService { /// <summary> /// 獲得所有的用戶信息 /// </summary> /// <returns>json或者xml</returns> [OperationContract] [WebGet(UriTemplate = "api/users", ResponseFormat = WebMessageFormat.Xml)] List<UserInfo> QueryList(); /// <summary> /// 根據id查詢用戶信息 /// </summary> /// <param name="where"></param> /// <returns></returns> [OperationContract] [WebGet(UriTemplate = "api/users/{where}", ResponseFormat = WebMessageFormat.Json)] UserInfo Query(string where); /// <summary> /// 根據編號刪除用戶信息 /// </summary> /// <param name="id"></param> /// <returns></returns> [OperationContract] [WebInvoke(UriTemplate = "api/users/{id}", Method = "DELETE", ResponseFormat = WebMessageFormat.Xml)] bool Delete(string id); /// <summary> /// 添加用戶信息 /// </summary> /// <param name="userInfo"></param> /// <returns></returns> [OperationContract] [WebInvoke(UriTemplate = "api/users", Method = "POST", ResponseFormat = WebMessageFormat.Xml, RequestFormat = WebMessageFormat.Json)] bool Insert(UserInfo userInfo); /// <summary> /// 更新用戶信息 /// </summary> /// <param name="userInfo"></param> /// <returns></returns> [OperationContract] [WebInvoke(UriTemplate = "api/users", Method = "PUT", ResponseFormat = WebMessageFormat.Xml, RequestFormat = WebMessageFormat.Json)] bool Update(UserInfo userInfo); } }
修改的地方:uriTemplate,以名詞組成,具體的操作通過Method謂詞進行區分,另外參數中UserInfo,為其WebInvoke設置RequestFormat(請求數據類型)屬性,設置為json格式的數據,這樣在服務端介紹到數據后,系統內部將對齊進行反序列化為具體的實體類。還有合並按照id和name查詢的接口,因為原來設置的uritemplate為api/users/{id}和api/users/{name}而且請求的方法都是get方法,無法區別到底調用哪個,所以將兩個方法合並為了一個方法。
1、刪除Http(delete),根據id找到用戶信息,並從集合中移除。
1 /// <summary> 2 /// 根據編號刪除用戶信息 3 /// </summary> 4 /// <param name="id"></param> 5 /// <returns></returns> 6 public bool Delete(string id) 7 { 8 //當前操作上下文 9 WebOperationContext woc = WebOperationContext.Current; 10 //狀態碼 11 woc.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK; 12 try 13 { 14 if (string.IsNullOrEmpty(id)) 15 { 16 throw new ArgumentNullException("id"); 17 } 18 var users = QueryList(); 19 int iId = Convert.ToInt32(id); 20 var user = users.Where(x => x.ID == iId).FirstOrDefault(); 21 woc.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.Accepted; 22 return users.Remove(user); 23 } 24 catch (Exception ex) 25 { 26 //異常 輸出狀態 27 woc.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.ExpectationFailed; 28 woc.OutgoingResponse.StatusDescription = ex.Message; 29 return false; 30 } 31 }
2、添加Http(post),POST里的數據格式通過RequestFormat定義為Json,WCF框架接受到Json數據的請求,會自動反序列化成UserInfo實例。
1 /// <summary> 2 /// 添加用戶信息 3 /// </summary> 4 /// <param name="userInfo"></param> 5 /// <returns></returns> 6 public bool Insert(UserInfo userInfo) 7 { 8 var users = QueryList(); 9 WebOperationContext woc = WebOperationContext.Current; 10 woc.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK; 11 try 12 { 13 users.Add(userInfo); 14 woc.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.Created; 15 return true; 16 } 17 catch (Exception ex) 18 { 19 //異常 輸出狀態 20 woc.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.ExpectationFailed; 21 woc.OutgoingResponse.StatusDescription = ex.Message; 22 return false; 23 } 24 }
3、修改(Http/Put),根據id找到要修改的對象,對該對象重新賦值。
1 /// <summary> 2 /// 更新用戶信息 3 /// </summary> 4 /// <param name="userInfo"></param> 5 /// <returns></returns> 6 public bool Update(UserInfo userInfo) 7 { 8 var users = QueryList(); 9 WebOperationContext woc = WebOperationContext.Current; 10 try 11 { 12 var user = users.Where(x => x.ID == userInfo.ID).FirstOrDefault(); 13 if (user != null) 14 { 15 user = userInfo; 16 woc.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.Accepted; 17 return true; 18 } 19 else 20 { 21 woc.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.NotFound; 22 return false; 23 } 24 25 } 26 catch (Exception ex) 27 { 28 //異常 輸出狀態 29 woc.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.ExpectationFailed; 30 woc.OutgoingResponse.StatusDescription = ex.Message; 31 return false; 32 } 33 }
4、查詢(http/get)
/// <summary> /// 獲得所有的用戶信息 /// </summary> /// <returns>json或者xml</returns> public List<UserInfo> QueryList() { return new List<UserInfo>() { new UserInfo() { ID = 1, Name = "wofly", Age = 22, Birthday = DateTime.Now, Gender = true }, new UserInfo() { ID = 2, Name = "san zhang", Age = 21, Birthday = DateTime.Now, Gender = true }, new UserInfo() { ID = 3, Name = "wukong sun", Age = 23, Birthday = DateTime.Now, Gender = false }, new UserInfo() { ID = 4, Name = "zi ma", Age = 45, Birthday = DateTime.Now, Gender = true } }; }
服務端的異常處理中通過 OutgoingResponse.StatusCode 返回不同的Code。
查詢成功 —— System.Net.HttpStatusCode.OK (默認)。
創建成功 —— System.Net.HttpStatusCode.Created
更新或者刪除成功 —— System.Net.HttpStatusCode.Accepted
當然,HttpStatusCode枚舉不止這幾個。感興趣的可以查看這個枚舉。你會發現,包攬了常見的一些狀態枚舉。
// Summary: // Contains the values of status codes defined for HTTP. public enum HttpStatusCode { // Summary: // Equivalent to HTTP status 100. System.Net.HttpStatusCode.Continue indicates // that the client can continue with its request. Continue = 100, // // Summary: // Equivalent to HTTP status 101. System.Net.HttpStatusCode.SwitchingProtocols // indicates that the protocol version or protocol is being changed. SwitchingProtocols = 101, // // Summary: // Equivalent to HTTP status 200. System.Net.HttpStatusCode.OK indicates that // the request succeeded and that the requested information is in the response. // This is the most common status code to receive. OK = 200, // // Summary: // Equivalent to HTTP status 201. System.Net.HttpStatusCode.Created indicates // that the request resulted in a new resource created before the response was // sent. Created = 201, // // Summary: // Equivalent to HTTP status 202. System.Net.HttpStatusCode.Accepted indicates // that the request has been accepted for further processing. Accepted = 202, // // Summary: // Equivalent to HTTP status 203. System.Net.HttpStatusCode.NonAuthoritativeInformation // indicates that the returned metainformation is from a cached copy instead // of the origin server and therefore may be incorrect. NonAuthoritativeInformation = 203, // // Summary: // Equivalent to HTTP status 204. System.Net.HttpStatusCode.NoContent indicates // that the request has been successfully processed and that the response is // intentionally blank. NoContent = 204, // // Summary: // Equivalent to HTTP status 205. System.Net.HttpStatusCode.ResetContent indicates // that the client should reset (not reload) the current resource. ResetContent = 205, // // Summary: // Equivalent to HTTP status 206. System.Net.HttpStatusCode.PartialContent indicates // that the response is a partial response as requested by a GET request that // includes a byte range. PartialContent = 206, // // Summary: // Equivalent to HTTP status 300. System.Net.HttpStatusCode.MultipleChoices // indicates that the requested information has multiple representations. The // default action is to treat this status as a redirect and follow the contents // of the Location header associated with this response. MultipleChoices = 300, // // Summary: // Equivalent to HTTP status 300. System.Net.HttpStatusCode.Ambiguous indicates // that the requested information has multiple representations. The default // action is to treat this status as a redirect and follow the contents of the // Location header associated with this response. Ambiguous = 300, // // Summary: // Equivalent to HTTP status 301. System.Net.HttpStatusCode.MovedPermanently // indicates that the requested information has been moved to the URI specified // in the Location header. The default action when this status is received is // to follow the Location header associated with the response. MovedPermanently = 301, // // Summary: // Equivalent to HTTP status 301. System.Net.HttpStatusCode.Moved indicates // that the requested information has been moved to the URI specified in the // Location header. The default action when this status is received is to follow // the Location header associated with the response. When the original request // method was POST, the redirected request will use the GET method. Moved = 301, // // Summary: // Equivalent to HTTP status 302. System.Net.HttpStatusCode.Found indicates // that the requested information is located at the URI specified in the Location // header. The default action when this status is received is to follow the // Location header associated with the response. When the original request method // was POST, the redirected request will use the GET method. Found = 302, // // Summary: // Equivalent to HTTP status 302. System.Net.HttpStatusCode.Redirect indicates // that the requested information is located at the URI specified in the Location // header. The default action when this status is received is to follow the // Location header associated with the response. When the original request method // was POST, the redirected request will use the GET method. Redirect = 302, // // Summary: // Equivalent to HTTP status 303. System.Net.HttpStatusCode.SeeOther automatically // redirects the client to the URI specified in the Location header as the result // of a POST. The request to the resource specified by the Location header will // be made with a GET. SeeOther = 303, // // Summary: // Equivalent to HTTP status 303. System.Net.HttpStatusCode.RedirectMethod automatically // redirects the client to the URI specified in the Location header as the result // of a POST. The request to the resource specified by the Location header will // be made with a GET. RedirectMethod = 303, // // Summary: // Equivalent to HTTP status 304. System.Net.HttpStatusCode.NotModified indicates // that the client's cached copy is up to date. The contents of the resource // are not transferred. NotModified = 304, // // Summary: // Equivalent to HTTP status 305. System.Net.HttpStatusCode.UseProxy indicates // that the request should use the proxy server at the URI specified in the // Location header. UseProxy = 305, // // Summary: // Equivalent to HTTP status 306. System.Net.HttpStatusCode.Unused is a proposed // extension to the HTTP/1.1 specification that is not fully specified. Unused = 306, // // Summary: // Equivalent to HTTP status 307. System.Net.HttpStatusCode.RedirectKeepVerb // indicates that the request information is located at the URI specified in // the Location header. The default action when this status is received is to // follow the Location header associated with the response. When the original // request method was POST, the redirected request will also use the POST method. RedirectKeepVerb = 307, // // Summary: // Equivalent to HTTP status 307. System.Net.HttpStatusCode.TemporaryRedirect // indicates that the request information is located at the URI specified in // the Location header. The default action when this status is received is to // follow the Location header associated with the response. When the original // request method was POST, the redirected request will also use the POST method. TemporaryRedirect = 307, // // Summary: // Equivalent to HTTP status 400. System.Net.HttpStatusCode.BadRequest indicates // that the request could not be understood by the server. System.Net.HttpStatusCode.BadRequest // is sent when no other error is applicable, or if the exact error is unknown // or does not have its own error code. BadRequest = 400, // // Summary: // Equivalent to HTTP status 401. System.Net.HttpStatusCode.Unauthorized indicates // that the requested resource requires authentication. The WWW-Authenticate // header contains the details of how to perform the authentication. Unauthorized = 401, // // Summary: // Equivalent to HTTP status 402. System.Net.HttpStatusCode.PaymentRequired // is reserved for future use. PaymentRequired = 402, // // Summary: // Equivalent to HTTP status 403. System.Net.HttpStatusCode.Forbidden indicates // that the server refuses to fulfill the request. Forbidden = 403, // // Summary: // Equivalent to HTTP status 404. System.Net.HttpStatusCode.NotFound indicates // that the requested resource does not exist on the server. NotFound = 404, // // Summary: // Equivalent to HTTP status 405. System.Net.HttpStatusCode.MethodNotAllowed // indicates that the request method (POST or GET) is not allowed on the requested // resource. MethodNotAllowed = 405, // // Summary: // Equivalent to HTTP status 406. System.Net.HttpStatusCode.NotAcceptable indicates // that the client has indicated with Accept headers that it will not accept // any of the available representations of the resource. NotAcceptable = 406, // // Summary: // Equivalent to HTTP status 407. System.Net.HttpStatusCode.ProxyAuthenticationRequired // indicates that the requested proxy requires authentication. The Proxy-authenticate // header contains the details of how to perform the authentication. ProxyAuthenticationRequired = 407, // // Summary: // Equivalent to HTTP status 408. System.Net.HttpStatusCode.RequestTimeout indicates // that the client did not send a request within the time the server was expecting // the request. RequestTimeout = 408, // // Summary: // Equivalent to HTTP status 409. System.Net.HttpStatusCode.Conflict indicates // that the request could not be carried out because of a conflict on the server. Conflict = 409, // // Summary: // Equivalent to HTTP status 410. System.Net.HttpStatusCode.Gone indicates that // the requested resource is no longer available. Gone = 410, // // Summary: // Equivalent to HTTP status 411. System.Net.HttpStatusCode.LengthRequired indicates // that the required Content-length header is missing. LengthRequired = 411, // // Summary: // Equivalent to HTTP status 412. System.Net.HttpStatusCode.PreconditionFailed // indicates that a condition set for this request failed, and the request cannot // be carried out. Conditions are set with conditional request headers like // If-Match, If-None-Match, or If-Unmodified-Since. PreconditionFailed = 412, // // Summary: // Equivalent to HTTP status 413. System.Net.HttpStatusCode.RequestEntityTooLarge // indicates that the request is too large for the server to process. RequestEntityTooLarge = 413, // // Summary: // Equivalent to HTTP status 414. System.Net.HttpStatusCode.RequestUriTooLong // indicates that the URI is too long. RequestUriTooLong = 414, // // Summary: // Equivalent to HTTP status 415. System.Net.HttpStatusCode.UnsupportedMediaType // indicates that the request is an unsupported type. UnsupportedMediaType = 415, // // Summary: // Equivalent to HTTP status 416. System.Net.HttpStatusCode.RequestedRangeNotSatisfiable // indicates that the range of data requested from the resource cannot be returned, // either because the beginning of the range is before the beginning of the // resource, or the end of the range is after the end of the resource. RequestedRangeNotSatisfiable = 416, // // Summary: // Equivalent to HTTP status 417. System.Net.HttpStatusCode.ExpectationFailed // indicates that an expectation given in an Expect header could not be met // by the server. ExpectationFailed = 417, // UpgradeRequired = 426, // // Summary: // Equivalent to HTTP status 500. System.Net.HttpStatusCode.InternalServerError // indicates that a generic error has occurred on the server. InternalServerError = 500, // // Summary: // Equivalent to HTTP status 501. System.Net.HttpStatusCode.NotImplemented indicates // that the server does not support the requested function. NotImplemented = 501, // // Summary: // Equivalent to HTTP status 502. System.Net.HttpStatusCode.BadGateway indicates // that an intermediate proxy server received a bad response from another proxy // or the origin server. BadGateway = 502, // // Summary: // Equivalent to HTTP status 503. System.Net.HttpStatusCode.ServiceUnavailable // indicates that the server is temporarily unavailable, usually due to high // load or maintenance. ServiceUnavailable = 503, // // Summary: // Equivalent to HTTP status 504. System.Net.HttpStatusCode.GatewayTimeout indicates // that an intermediate proxy server timed out while waiting for a response // from another proxy or the origin server. GatewayTimeout = 504, // // Summary: // Equivalent to HTTP status 505. System.Net.HttpStatusCode.HttpVersionNotSupported // indicates that the requested HTTP version is not supported by the server. HttpVersionNotSupported = 505, }
因為REST 是基於HTTP的, 所以對於 REST 的客戶端的開發者,無法像使用傳統的 WebService或者其他的WCF服務通過引用wsdl,享受“奢侈”的代碼生成,而使用強類型的本地代理調用服務。開發者只能使用Http請求來進行請求,而使得客戶端真正是語言無關的。
Microsoft.Http.dll 和 Microsoft.Http.Extensions.dll,它們是微軟提供的REST客戶端包。可以更加方便地操作 HttpRequest/Response,你可以在這里下到: http://aspnet.codeplex.com/releases/view/24644
下載:WCF REST Starter Kit Preview 2.msi
安裝之后,在這里C:\Program Files (x86)\Microsoft WCF REST\WCF REST Starter Kit Preview 2\Assemblies(默認安裝的) 你可以找到相關的程序集

代碼片段
查詢:HttpClient.Get方法發送get請求,返回的是HttpResponseMessage,HttpResponseMessage.Content 返回的是你在服務端設置ResponseFormat = WebMessageFormat.Xml數據,當然也可以返回json數據。
var client = new HttpClient(); var strUrl = "http://localhost:21074/userInfo/api/users"; var response = client.Get(strUrl); response.EnsureStatusIsSuccessful(); var xml = response.Content.ReadAsString(); Console.WriteLine(xml); Console.Read();

添加數據,將數據序列化成Json格式放進 HttpContent 再使用 HttpClient.Post 提交 HttpContent 數據。
HttpContent 需要指定 ContentType 是Json格式的。
var client = new HttpClient(); var strPostUrl = "http://localhost:21074/userInfo/api/users"; var postData = new { ID = 5, Name = "zhang san", Gender = true, Birthday = DateTime.Now, Age = 23 }; System.Web.Script.Serialization.JavaScriptSerializer jss = new JavaScriptSerializer(); HttpContent content = HttpContent.Create(Encoding.UTF8.GetBytes(jss.Serialize(postData)),"application/json"); HttpResponseMessage responseMessage = client.Post(strPostUrl, content); Console.WriteLine(responseMessage.EnsureStatusIsSuccessful().StatusCode);
服務端
客戶端

更新數據:找到id為1的用戶信息,並修改。
var client = new HttpClient(); var strPostUrl = "http://localhost:21074/userInfo/api/users"; var postData = new { ID = 1, Name = "zhang san", Gender = true, Birthday = DateTime.Now, Age = 22 }; System.Web.Script.Serialization.JavaScriptSerializer jss = new JavaScriptSerializer(); HttpContent content = HttpContent.Create(Encoding.UTF8.GetBytes(jss.Serialize(postData)), "application/json"); HttpResponseMessage responseMessage = client.Put(strPostUrl, content); Console.WriteLine(responseMessage.EnsureStatusIsSuccessful().StatusCode);

客戶端

刪除數據:找到id為1的,刪除它
var client = new HttpClient(); var strPostUrl = "http://localhost:21074/userInfo/api/users/{0}"; HttpResponseMessage responseMessage = client.Delete(string.Format(strPostUrl,1)); Console.WriteLine(responseMessage.EnsureStatusIsSuccessful().StatusCode); Console.Read();

總結
這里將restful的增刪改查操作重新修改了下,需要注意的地方是,操作的處理是以Method方法而定,而不是url,post(增加),get(查詢),delete(刪除),put(更改),通過上面的例子你也會發現,請求的url相同,但是不同的請求謂詞(post,get,delete,put)帶來的操作並不相同。另外本文也介紹了客戶但HttpClient的使用。
參考文章:
http://blog.csdn.net/fangxing80/article/details/6247297
