什麼是 REST

REST (Representational State Transfer, 表徵狀態轉移)

REST 是一種軟體架構風格,希望網路上資源都可以用 URL 來指定,讓客戶端可以直接透過 URL 對資源進行 CRUD (創建、獲取、修改、刪除)操作。 RE 是 Representational 的縮寫,它代表由 Client 端透過 URI 取得的資源的具體象徵 (Representational), 應用程式可以依據取得的具體象徵來轉變其狀態(在瀏覽器上就是畫面的變化), 這樣不斷的反覆過程就是所謂的 Representational State Transfer。 維基百科:REST

JSON (Javascript Object Notation)

JSON 是一種以純文字為基礎的資料格式。它也是 REST 服務預設的訊息回應格式。 因為它的結構簡單,很適合用來做為程式溝通或交換資料時使用。 也由於它的輕量化和易於閱讀的特性,目前已廣泛應用於各種技術領域,例如:AJAX, WCF, jQuery。

你可以透過特定的格式去儲存任何資料(字串,數字,陣列,物件),也可以透過物件或陣列來傳送較複雜的資料。 底下是一個簡單的 JSON 範例:

{
"proudctName": "Computer Monitor",
"price": "229.00",
"specifications": 
{
"size": 22,
"type": "LCD",
"colors": ["black", "red", "white"]
}
}

建立 REST WCF Service

用來建置 Windows Communication Foundation (WCF) 服務與用戶端應用程式時所需的型別,都包含在 System.ServiceModel 命名空間。 底下幾個相關的命名空間:

要設計 REST 服務,必須引用 System.ServiceModel.Web 命名空間。
該命名空間定義在 System.ServiceModel.Web.dll 組件裡,必須手動加入參考。
必須注意的是,Framework 版本必須選用完整版(如3.5或4.0 full profile),若選用 client profile 會找不到這個 dll。

建立支援 REST 的 WCF 服務

由於.NET 3.5 的支援,我們可以透過相當簡單的方式來建立 REST 的 WCF 服務,若開發人員想透過WCF技術,來開發 RESTful 的 Service,只需要在VS2008 SP1的專案範本中,選擇 「AJAX-enabled WCF Service」 即可:

「AJAX-enabled WCF Service」項目的內容

底下是一個 「AJAX-enabled WCF Service」的簡單範例,當新增一個 「AJAX-enabled WCF Service」項目時,下面幾點注意事項:

  • 會自動加入 AspNetCompatibilityRequirements 屬性。
    若設定成允許 ASP.NET相容性模式,則 WCF 服務會與 ASP.NET 使用相同的管線,類似於 ASMX 服務的方式。 在此模式下, WCF 服務也可提供 ASP.NET 檔案授權、 UrlAuthorization 和 HTTP 工作階段狀態等功能。
  • 預設的 ResponseFormatWebMessageFormat.Json
    若要傳回 XML ,必須加入 [WebGet(ResponseFormat=WebMessageFormat.Xml)] 屬性宣告。
  • WebInvoke :這屬性可用來設定 HTTP 動詞(例如:POST, PUT, DELETE),預設使用 HTTP POST 方式叫用。
  • WebGet :這屬性是用來設定使用 HTTP GET 叫用。這種方法的回傳結果支援快取。
  • 在第3行的 Namespace 中要設定一個值。 ```c# namespace WcfService2 { [ServiceContract(Namespace = “WcfService2”)] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class HRService { //一個方法若沒有特別宣告屬性,預設值有: //1. 必須使用 HTTP POST 叫用 //2. 回傳 Json 格式

[OperationContract] public Employee Employee1(string EmpID) { Employee emp = new Employee() { EmployeeID = EmpID, FirstName = “vito”, LastName = “shao”, Age = 30 }; return emp; }

[OperationContract] [WebGet] public Employee Employee1(string EmpID) { Employee emp = new Employee() { EmployeeID = EmpID, FirstName = “vito”, LastName = “shao”, Age = 30 }; return emp; }

[OperationContract] [WebGet] public Employee Employee2(string EmpID) { Employee emp = new Employee() { EmployeeID = EmpID, FirstName = “vito”, LastName = “shao”, Age = 30 }; return emp; } }

public class Employee { public string EmployeeID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } } }


#### 「AJAX-enabled WCF Service」項目的組態設定

在這個設定內容裡,與前一節中的 WCF 服務最大的不同在於:  

- 端點行為中加入 **enableWebScript** 屬性。它表示這個 endpoint 是一個支援 **JSON** 資料格式的 **RESTful** 服務,因此可以透過 AJAX 直接叫用。
- 端點的繫結方式採用 **webHttpBinding**。它指出該服務必須透過 HTTP 呼叫,而不是透過 SOAP 呼叫。
```xml
<configuration>

<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />

<behaviors>
<endpointBehaviors>
<behavior name="WcfService2.HRServiceAspNetAjaxBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
</behaviors>

<services>
<service name="WcfService2.HRService">
<endpoint address="" 
binding="webHttpBinding" 
contract="WcfService2.HRService" 
behaviorConfiguration="WcfService2.HRServiceAspNetAjaxBehavior"/>
</service>
</services>
</system.serviceModel>

</configuration>

叫用 REST WCF Service

透過 ASP.NET AJAX ScriptManager 叫用 REST WCF Service

不曉得為什麼,在 ScriptManager 底下叫用 WCF 服務,該服務合約的 namespace 屬性值就必須指定為類別的命名空間,才可以成功呼叫。

namespace WcfService2
{
[ServiceContract(Namespace = "WcfService2")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class HRService
{

}

其用戶端的叫用方法如下:

<head id="Head1" runat="server">
<script language="javascript" type="text/javascript">
function Button1_onclick() {
var service = new WcfService2.HRService();
var resutl = service.GetEmployee('001', onSuccess, onFail, null);
}
function onSuccess(result) {
alert(result.FirstName + ' ' + result.LastName);
}
function onFail(result) {
alert(result);
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>         
<asp:ServiceReference Path="HRService.svc" />       
</Services> 
</asp:ScriptManager>

<input id="Button1" type="button" value="invoke .svc with ajax" onclick="return Button1_onclick()" />
</div>
</form>
</body>

透過 jQuery 叫用 REST WCF Service

很奇怪,若使用 jQuery 叫用,該服務合約就不用指定 namespace 屬性。

其用戶端的叫用方法如下:

<head id="Head1" runat="server">
<title></title>
<script type="text/javascript" src="./Scripts/jquery-1.8.2.js"></script>

<script language="javascript" type="text/javascript">
function btnAjax_Post_onclick() {
jQuery.support.cors = true;

var empid = $("#txtEmpID").val();
var data = '{"EmpID":"' + empid + '"}';
var uri = "http://localhost:30302/HRService.svc/Employee1";

$.ajax({
type: "POST",
url: uri,
dataType: "json",
data: data,
contentType: "application/json; charset=utf-8",
success: OnSuccess,
error: OnFail
});
}

function btnAjax_Get_onclick() {
jQuery.support.cors = true;
var empid = $("#txtEmpID").val();
var uri = "http://localhost:30302/HRService.svc/Employee2?EmpID=" + empid;

$.ajax({
type: "GET",
url: uri,
dataType: "json",
contentType: "application/json; charset=utf-8",
success: OnSuccess,
error: OnFail
});
}
function OnSuccess(result) {
alert(result.d.EmployeeID + ' ' + result.d.FirstName + ' ' + result.d.LastName);
}
function OnFail(result) {
alert('Service call failed: ' + result.status + '' + result.statusText);
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
EmpID: <input id="txtEmpID" type="text" value="007" /><br /><br />

<input id="btnAjax_Post" type="button" value="invoke .svc with ajax post" onclick="return btnAjax_Post_onclick()"/><br /><br />

<input id="btnAjax_Get" type="button" value="invoke .svc with ajax get" onclick="return btnAjax_Get_onclick()" /><br /><br />
</div>
</form>
</body>

底下是幾個 jQuery 用來叫用服務的方法:

  • jQuery.ajax :Perform an asynchronous HTTP (Ajax) request.
  • jQuery.getJSON :Load JSON-encoded data from the server using a HTTP GET request.
  • jQuery.post :Load data from the server using a HTTP POST request.
  • jQuery.get :Load data from the server using a HTTP GET request. ```javascript jQuery.ajax( url [, settings] )

jQuery.getJSON( url [, data] [, success(data, textStatus, jqXHR)] ) //Returns: jqXHR

jQuery.post( url [, data] [, success(data, textStatus, jqXHR)] [, dataType] ) //Returns: jqXHR

jQuery.get( url [, data ] [, success(data, textStatus, jqXHR) ] [, dataType ] ) //Returns: jqXHR


要注意一點,在 jQuery 1.4X 以後,叫用 jQuery 執行方法時,預設就不允許 cross-site 。如果要執行跨網站的方法,必須加入以下程式碼:
```javascript
jQuery.support.cors = true;

套用 UriTemplate 叫用 REST WCF Service

使用 webHttp 端點行為

要使用 UriTemplate 必須使用 <webHttp> 端點行為,而不是使用 <enableWebScript> 端點行為。

<configuration>

<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />

<behaviors>
<endpointBehaviors>
<behavior name="WcfService3.HRServiceAspNetAjaxBehavior">
<webHttp helpEnabled="true" />
</behavior>
</endpointBehaviors>
</behaviors>

<services>
<service name="WcfService3.HRService">
<endpoint address="" 
binding="webHttpBinding" 
contract="WcfService3.HRService" 
behaviorConfiguration="WcfService3.HRServiceAspNetAjaxBehavior"/>
</service>
</services>
</system.serviceModel>

</configuration>

定義 UriTemplate

UriTemplate 是指這一個 RESTful 服務的呼叫方式(嚴格說起來是描述該資源的Uri位置),是要以何種 Uri 來表達,透過這種方式建立出來的 WCF 服務,就直接支援了 REST 型態的遠端呼叫方式。 例如,上例中我們給定的『UriTemplate』是”HR/EmpId/{EmpID}”,則這個資源可以透過底下的網址來取得:
http://localhost:30303/HRService.svc/HR/EmpId/001

namespace WcfService3
{
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class HRService
{
[OperationContract]
[WebInvoke(
Method = "POST",
UriTemplate = "HR/EmpId/{EmpID}",
RequestFormat = WebMessageFormat.Json, 
ResponseFormat = WebMessageFormat.Json, 
BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public Employee Employee1(string EmpID)
{
Employee emp = new Employee() { EmployeeID = EmpID+"post", FirstName = "vito", LastName = "shao", Age = 30 };
return emp;
}

[OperationContract]
[WebGet(
UriTemplate = "HR/EmpId/{EmpID}",
RequestFormat = WebMessageFormat.Json, 
ResponseFormat = WebMessageFormat.Json, 
BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public Employee Employee2(string EmpID)
{
Employee emp = new Employee() { EmployeeID = EmpID + "get", FirstName = "vito", LastName = "shao", Age = 30 };
return emp;
}
}

public class Employee
{
public string EmployeeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
}

使用 jQuery 方法叫用服務

<script language="javascript" type="text/javascript">            function btnAjax_Post_onclick() {            jQuery.support.cors = true;
var empid = $("#txtEmpID").val();              var data = '{"EmpID":"' + empid + '"}';              var uri = "http://localhost:30303/HRService.svc/HR/EmpId/" + empid;                $.ajax({                  type: "POST",                  url: uri,                  dataType: "json",                  data: data,                  contentType: "application/json; charset=utf-8",                  success: OnSuccess,                  error: OnFail              });          }            function btnAjax_Get_onclick() {            jQuery.support.cors = true;
var empid = $("#txtEmpID").val();              var uri = "http://localhost:30303/HRService.svc/HR/EmpId/" + empid;                $.ajax({                  type: "GET",                  url: uri,                  dataType: "json",                  contentType: "application/json; charset=utf-8",                  success: OnSuccess,                  error: OnFail              });          }            function btnPost_onclick() {            jQuery.support.cors = true;
var empid = $("#txtEmpID").val();              var uri = "http://localhost:30303/HRService.svc/HR/EmpId/" + empid;                $.post(uri, null, OnSuccess);          }            function btnGet_onclick() {            jQuery.support.cors = true;
var empid = $("#txtEmpID").val();              var uri = "http://localhost:30303/HRService.svc/HR/EmpId/" + empid;                $.get(uri, null, OnSuccess);          }            function OnSuccess(result) {              alert(result.EmployeeID + ' ' + result.FirstName + ' ' + result.LastName);          }          function OnFail(result) {              alert('Service call failed: ' + result.status + '' + result.statusText);          }        </script>  

參考資料