Rest API 開發

 

概述php

REST 從資源的角度來觀察整個網絡,分佈在各處的資源由URI肯定,而客戶端的應用經過URI來獲取資源的表示方式。得到這些表徵導致這些應用程序轉變了其狀態。隨着不斷獲取資源的表示方式,客戶端應用不斷地在轉變着其狀態,所謂表述性狀態轉移(Representational State Transfer)。前端

這一觀點不是憑空臆造的,而是經過觀察當前Web互聯網的運做方式而抽象出來的。Roy Fielding 認爲,java

「設計良好的網絡應用表現爲一系列的網頁,這些網頁能夠看做的虛擬的狀態機,用戶選擇這些連接致使下一網頁傳輸到用戶端展示給使用的人,而這正表明了狀態的轉變。」web

REST是設計風格而不是標準。REST一般基於使用HTTP,URI,和XML以及HTML這些現有的普遍流行的協議和標準。json

  • 資源是由URI來指定。
  • 對資源的操做包括獲取、建立、修改和刪除資源,這些操做正好對應HTTP協議提供的GET、POST、PUT和DELETE方法。
  • 經過操做資源的表現形式來操做資源。
  • 資源的表現形式則是XML或者HTML,取決於讀者是機器仍是人,是消費web服務的客戶軟件仍是web瀏覽器。固然也能夠是任何其餘的格式。
REST的要求
  • 客戶端和服務器結構
  • 鏈接協議具備無狀態性
  • 可以利用Cache機制增進性能
  • 層次化的系統
  • 隨需代碼 - Javascript (可選)

 

RESTful Web 服務設計模式

RESTful Web 服務(也稱爲 RESTful Web API)是一個使用HTTP並遵循REST原則的Web服務。它從如下三個方面資源進行定義:URI,好比:http://example.com/resources/。瀏覽器

§ Web服務接受與返回的互聯網媒體類型,好比:JSON,XML ,YAML 等。緩存

§ Web服務在該資源上所支持的一系列請求方法(好比:POST,GET,PUT或DELETE)。安全

該表列出了在實現RESTful Web 服務時HTTP請求方法的典型用途。服務器

HTTP 請求方法在RESTful Web 服務中的典型應用

 

資源

GET

PUT

POST

DELETE

一組資源的URI,好比http://example.com/resources/

列出 URI,以及該資源組中每一個資源的詳細信息(後者可選)。

使用給定的一組資源替換當前整組資源。

在本組資源中建立/追加一個新的資源。 該操做每每返回新資源的URL。

刪除 整組資源。

單個資源的URI,好比http://example.com/resources/142

獲取 指定的資源的詳細信息,格式能夠自選一個合適的網絡媒體類型(好比:XML、JSON等)

替換/建立 指定的資源。並將其追加到相應的資源組中。

把指定的資源當作一個資源組,並在其下建立/追加一個新的元素,使其隸屬於當前資源。

刪除 指定的元素。

 

PUT 和 DELETE 方法是冪等方法。GET方法是安全方法 (不會對服務器端有修改,所以也是冪等的)。

不像基於SOAP的Web服務,RESTful Web服務並無的「正式」標準。 這是由於REST是一種架構,而SOAP只是一個協議。雖然REST不是一個標準,但在實現RESTful Web服務時可使用其餘各類標準(好比HTTP,URL,XML,PNG等)。

REST的優勢

  • 能夠利用緩存Cache來提升響應速度
  • 通信自己的無狀態性可讓不一樣的服務器的處理一系列請求中的不一樣請求,提升服務器的擴展性
  • 瀏覽器便可做爲客戶端,簡化軟件需求
  • 相對於其餘疊加在HTTP協議之上的機制,REST的軟件依賴性更小
  • 不須要額外的資源發現機制
  • 在軟件技術演進中的長期的兼容性更好

 

Rest 開發

首先先定義接口IRestHandler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/// <summary>
/// The IRestHandler is an interface which provides Delete,Get,Post and Put methods.
/// </summary>
public  interface  IRestHandler : ICloneable
{
     /// <summary>
     /// Delete method for RestHandler
     /// </summary>
     /// <param name="processor">The rest processor.</param>
     /// <param name="authenticated">if set to <c>true</c> [authenticated].</param>
     /// <returns>The http response</returns>
     RestHandlerResponse Delete(IRestProcessor processor, bool  authenticated);
 
     /// <summary>
     /// Get method for RestHandler
     /// </summary>
     /// <param name="processor">The rest processor.</param>
     /// <param name="authenticated">if set to <c>true</c> [authenticated].</param>
     /// <returns>The http response</returns>
     RestHandlerResponse Get(IRestProcessor processor, bool  authenticated);
 
     /// <summary>
     /// Post method for RestHandler
     /// </summary>
     /// <param name="processor">The rest processor.</param>
     /// <param name="authenticated">if set to <c>true</c> [authenticated].</param>
     /// <returns>The http response</returns>
     RestHandlerResponse Post(IRestProcessor processor, bool  authenticated);
 
     /// <summary>
     /// Put method for RestHandler
     /// </summary>
     /// <param name="processor">The rest processor.</param>
     /// <param name="authenticated">if set to <c>true</c> [authenticated].</param>
     /// <returns>The http response</returns>
     RestHandlerResponse Put(IRestProcessor processor, bool  authenticated);
}

咱們要定義一個HttpListener,先定義一個接口IRestListener:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/// <summary>
/// Listen an ip point and accept connection.
/// </summary>
public  interface  IRestListener : IDisposable
{
     /// <summary>
     /// Gets or sets the max allowed connections to this listener.
     /// </summary>
     int  MaxConnections { get ; set ; }
 
     /// <summary>
     /// Gets or sets desktop rest manager.
     /// </summary>
     DesktopRestManager DesktopRestManager { get ; set ; }
 
     /// <summary>
     /// Gets a value that indicate if it is listening.
     /// </summary>
     bool  IsRunning { get ; }
 
     /// <summary>
     /// Gets or sets the server address information.
     /// </summary>
     IPEndPoint ServerAddress { get ; set ; }
 
     string  Protocol { get ; set ; }
 
     /// <summary>
     /// Start a listener.
     /// </summary>
     /// <returns>The ip end point to listen.</returns>
     bool  Start(TcpListener listener, IPEndPoint address);
 
     /// <summary>
     /// Stop the listener.
     /// </summary>
     /// <returns>True if successfully, else false.</returns>
     bool  Stop();
}

 

 

接下來實現

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
public  class  HttpListener : IRestListener
     {
         public  HttpListener(DesktopRestManager drm)
         {
             this .desktopRestManager = drm;
             this .isRunning = false ;          
             MaxConnections = 50;
             this .serverAddress = new  IPEndPoint( new  IPAddress( new  byte [] { 127, 0, 0, 1 }), 10000);
             this .Protocol = "Http" ;          
         }
 
         #region IRestServer Members
 
         //public event ServerStatusChangedHandler ServerStatusChanged;
 
         public  DesktopRestManager DesktopRestManager
         {
             get  { return  this .desktopRestManager; }
             set  { this .desktopRestManager = value; }
         }
 
         public  bool  IsRunning
         {
             get  { return  this .isRunning; }
         }
 
         public  IPEndPoint ServerAddress
         {
             get  { return  this .serverAddress; }
             set  { this .serverAddress = value; }
         }
 
         public  int  MaxConnections
         {
             get ;
             set ;
         }
 
         public  string  Protocol
         {
             get ;
             set ;
         }
 
 
         public  bool  Start(TcpListener tcpListener, IPEndPoint address)
         {
             this .ServerAddress = address;
             this .listener = tcpListener;
             this .isRunning = true ;
             Thread th = new  Thread( new  ThreadStart( this .Listening));
             th.Start();
             return  true ;
         }
 
         public  bool  Stop()
         {
             bool  success = true ; ;
             if  ( this .isRunning == true )
             {
                 try
                 {
                     this .isRunning = false ;
                     if  (listener != null )
                     {
                         listener.Stop();
                     }
                 }
                 catch  (SocketException socketEx)
                 {
                     _traceLog.InfoFormat( "Stop http rest server: {0}" , socketEx.Message);
                     success = false ;
                 }
                 
             }
             return  success;
         }
 
         #endregion
 
         #region IDisposable Members
 
         public  void  Dispose()
         {
             this .Stop();
         }
 
         #endregion
 
         #region Private Methods
         private  void  Listening()
         {
             while  ( this .isRunning)
             {
                 TcpClient tcpClient = null ;
                 try
                 {
                     tcpClient = listener.AcceptTcpClient();
                     HttpConnection connection = new  HttpConnection(tcpClient);
                     RestProcessor rh = new  RestProcessor( this .desktopRestManager);
                     Thread processThread = new  Thread( new  ParameterizedThreadStart(req => connection.SendResponse(rh.HandleRequest(req as  RestHandlerRequest))));
                     processThread.Name = "RestManager_Http_ProcessRequest" ;
                     processThread.Start(connection.GetRequest());
                 }
                 catch  (SocketException socketEx)
                 {
                     if  ( this .isRunning)
                     {
                         _traceLog.InfoFormat( "Socket exception: {0}" , socketEx.Message);
                     }
                     else
                     {
                         _traceLog.Info( "The use stop the http listener." );
                     }
                     if  (tcpClient != null  && tcpClient.Connected)
                     {
                         tcpClient.Close();
                     }
                 }
                 catch  (System.ArgumentNullException ex)
                 {
                     _traceLog.ErrorFormat( "Error occured: {0}" , ex.Message);
                 }
                 catch  (System.OutOfMemoryException ex)
                 {
                     _traceLog.ErrorFormat( "Error occured: {0}" , ex.Message);
                 }
                 catch  (System.Threading.ThreadStateException ex)
                 {
                     _traceLog.ErrorFormat( "Error occured: {0}" , ex.Message);
                 }
                 catch  (System.InvalidOperationException ex)
                 {
                     _traceLog.ErrorFormat( "Error occured: {0}" , ex.Message);
                 }
                 catch  (ApplicationException ex)
                 {
                     _traceLog.ErrorFormat( "Error occured: {0}" , ex.Message);
                 }
             }
             this .Stop();
         }
 
         #endregion
 
         #region Private Members
         private  DesktopRestManager desktopRestManager;
         private  bool  isRunning;
         private  IPEndPoint serverAddress;
         private  TcpListener listener;
         private  static  LogManager _traceLog = new  LogManager( "RestManager-HttpListener" );
         #endregion
     }

 

 

接下來處理HandleRequest:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/// <summary>
         /// Handles an http request for an Api call.
         /// </summary>
         public  RestHandlerResponse HandleRequest(RestHandlerRequest rhr)
         {
             RestHandlerResponse res;
             // 50 Requests in maximum
             if  (! this .restProcessorSemaphore.WaitOne(0))
             {
                 
                res = new  RestHandlerResponse(503);
             }
 
             else
             {
                 try
                 {
                     // There is no need decode the url here, since the address will be decoded when it is parsed.
                     //rhr.Address = System.Web.HttpUtility.UrlDecode(rhr.Address);
 
                     res = this .process(rhr);
 
                 }
                 catch  (RestManagerException ex)
                 {
                     traceLog.ErrorFormat( "Error happened while processing request\n{1}.\nException info:\n{0}  " ,ex.Message);
                     res = new  RestHandlerResponse(500);
                 }
                 try
                 {
                     this .restProcessorSemaphore.Release();
                 }
                 catch  (System.Threading.SemaphoreFullException)
                 {
                     traceLog.ErrorFormat( "Error happened while processing Semaphore.Release" );
                 }
                 catch  (System.IO.IOException)
                 {
                     traceLog.ErrorFormat( "Error happened while processing Semaphore.Release" );
                 }
                 catch  (System.UnauthorizedAccessException)
                 {
                     traceLog.ErrorFormat( "Error happened while processing Semaphore.Release" );
                 }
             }
 
             return  res;
         }

 

 

接下來咱們寫發送請求代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private  JObject MakeRequest( string  url)
{
     var  subsequentRequest = WebRequest.Create(url) as  HttpWebRequest;
     subsequentRequest.Timeout = 30000;
     subsequentRequest.Headers.Add( "Authorization" , "OAuth "  + TestToken);
     subsequentRequest.Headers.Add( "App-User" , TestUserName);
 
     WebResponse subsequentResponse;
 
     try
     {
         subsequentResponse = subsequentRequest.GetResponse();
         Stream stream = subsequentResponse.GetResponseStream();
         StreamReader sr = new  StreamReader(stream);
         string  output = sr.ReadToEnd();
         JObject jsonStr = JObject.Parse(output);
         return  jsonStr;
 
     }
     catch  (WebException ex)
     {
         if  (ex.Response != null )
         {
             HttpWebResponse errorResponse = (HttpWebResponse)ex.Response;
             StreamReader reader = new  StreamReader(errorResponse.GetResponseStream());
             string  output = reader.ReadToEnd();
             JObject jsonStr = JObject.Parse(output);
             return  jsonStr;
         }
         else
         {
             return  null ;
         }
     }
}

 

 

涉及項目的緣由,代碼只能提供這麼多了,僅供參考

Json的返回結果格式以下,:

1
[{ "CreatedDate" : "//Date(1299687080328+0800)//" , "Detail" : "Do Something 1" , "Title" : "Task1" },{ "CreatedDate" : "//Date(1299687080328+0800)//" , "Detail" : "Do Something 5" , "Title" : "Task5" }]

 

REST vs SOAP 
成熟度: 
SOAP雖然發展到如今已經脫離了初衷,可是對於異構環境服務發佈和調用,以及廠商的支持都已經達到了較爲成熟的狀況。不一樣平臺,開發語言之間經過SOAP來交互的web service都可以較好的互通(在部分複雜和特殊的參數和返回對象解析上,協議沒有做很細緻的規定,致使仍是須要做部分修正)

REST國外不少大網站都發布了本身的開發API,不少都提供了SOAP和REST兩種Web Service,根據調查部分網站的REST風格的使用狀況要高於SOAP。可是因爲REST只是一種基於Http協議實現資源操做的思想,所以各個網站的REST實現都自有一套,在後面會講訴各個大網站的REST API的風格。也正是由於這種各自實現的狀況,在性能和可用性上會大大高於SOAP發佈的web service,但統一通用方面遠遠不及SOAP。因爲這些大網站的SP每每專一於此網站的API開發,所以通用性要求不高。

因爲沒有相似於SOAP的權威性協議做爲規範,REST實現的各類協議僅僅只能算是私有協議,固然須要遵循REST的思想,可是這樣細節方面有太多沒有約束的地方。REST往後的發展所走向規範也會直接影響到這部分的設計是否可以有很好的生命力。

總的來講SOAP在成熟度上優於REST。

效率和易用性: 
       SOAP協議對於消息體和消息頭都有定義,同時消息頭的可擴展性爲各類互聯網的標準提供了擴展的基礎,WS-*系列就是較爲成功的規範。可是也因爲SOAP因爲各類需求不斷擴充其自己協議的內容,致使在SOAP處理方面的性能有所降低。同時在易用性方面以及學習成本上也有所增長。

       REST被人們的重視,其實很大一方面也是由於其高效以及簡潔易用的特性。這種高效一方面源於其面向資源接口設計以及操做抽象簡化了開發者的不良設計,同時也最大限度的利用了Http最初的應用協議設計理念。同時,在我看來REST還有一個很吸引開發者的就是可以很好的融合當前Web2.0的不少前端技術來提升開發效率。例如不少大型網站開放的REST風格的API都會有多種返回形式,除了傳統的xml做爲數據承載,還有(JSON,RSS,ATOM)等形式,這對不少網站前端開發人員來講就可以很好的mashup各類資源信息。

       所以在效率和易用性上來講,REST更勝一籌。

安全性: 
       這點其實能夠放入到成熟度中,不過在當前的互聯網應用和平臺開發設計過程當中,安全已經被提到了很高的高度,特別是做爲外部接口給第三方調用,安全性可能會高過業務邏輯自己。

       SOAP在安全方面是經過使用XML-Security和XML-Signature兩個規範組成了WS-Security來實現安全控制的,當前已經獲得了各個廠商的支持,.net ,php ,java 都已經對其有了很好的支持(雖然在一些細節上仍是有不兼容的問題,可是互通基本上是能夠的)。

       REST沒有任何規範對於安全方面做說明,同時如今開放REST風格API的網站主要分紅兩種,一種是自定義了安全信息封裝在消息中(其實這和SOAP沒有什麼區別),另一種就是靠硬件SSL來保障,可是這隻可以保證點到點的安全,若是是須要多點傳輸的話SSL就無能爲力了。安全這塊其實也是一個很大的問題,今年在BEA峯會上看到有演示採用SAML2實現的網站間SSO,實際上是直接採用了XML-Security和XML-Signature,效率看起來也不是很高。將來REST規範化和通用化過程當中的安全是否也會採用這兩種規範,是未知的,可是加入的越多,REST失去它高效性的優點越多。

應用設計與改造: 
       咱們的系統要麼就是已經有了那些須要被髮布出去的服務,要麼就是剛剛設計好的服務,可是開發人員的傳統設計思想讓REST的形式被接受還須要一點時間。同時在資源型數據服務接口設計上來講按照REST的思想來設計相對來講要容易一些,而對於一些複雜的服務接口來講,可能強要去按照REST的風格來設計會有些牽強。這一點其實能夠看看各大網站的接口就能夠知道,不少網站還要傳入function的名稱做爲參數,這就明顯已經違背了REST自己的設計思路。

       而SOAP自己就是面向RPC來設計的,開發人員十分容易接受,因此不存在什麼適應的過程。

總的來講,其實仍是一個老觀念,適合的纔是最好的 
       技術沒有好壞,只有是否是合適,一種好的技術和思想被誤用了,那麼就會獲得反效果。REST和SOAP各自都有本身的優勢,同時若是在一些場景下若是去改造REST,其實就會走向SOAP(例如安全)。

       REST對於資源型服務接口來講很合適,同時特別適合對於效率要求很高,可是對於安全要求不高的場景。而SOAP的成熟性能夠給須要提供給多開發語言的,對於安全性要求較高的接口設計帶來便利。因此我以爲純粹說什麼設計模式將會佔據主導地位沒有什麼意義,關鍵仍是看應用場景。

       同時很重要一點就是不要扭曲了REST如今不少網站都跟風去開發REST風格的接口,其實都是在學其形,不知其心,最後弄得不三不四,性能上不去,安全又保證不了,徒有一個看似象摸象樣的皮囊。

 

參考文獻:

http://blog.csdn.net/terryzero/article/details/6295855

http://zh.wikipedia.org/wiki/REST

相關文章
相關標籤/搜索