建立以 API 為中心的 Web 應用
正計劃著要開始搞一個新的網路應用?在這篇教程中,我們將討論如何建立以API為中心的網路應用,還會解釋在今天的多平臺世界,這類應用為什麼是重要的。
引言
API?
對於還不甚熟悉這個術語的朋友,API是Application Programming Interface(應用程式設計介面)的簡稱。根據維基百科:
API是以原始碼為基礎的約定,它用於軟體元件之間相互通訊的介面。API可能包含函式、資料結構、物件類、以及變數等的約定。
API視覺化
簡單地講,API指的是一組應用中的函式,它們能夠被其它應用(或者這些函式所屬應用自己,下文中我們將會看到)用來與應用進行互動。API是一種很棒的向外部應用安全和妥善地表明其功能的方式,因為這些外部應用所能利用的所有功能僅限於API中所表現出的功能。
什麼是“以API為中心的”網路應用?
以API為中心的網路應用就是基本上通過API呼叫執行大多數甚或所有功能的一類網路應用。
以API為中心的網路應用就是基本上通過API呼叫執行大多數甚或所有功能的一類網路應用。舉個例子,如果你正要登入一個使用者,你應當將其認證資訊傳送給API,然後API會向你返回一個結果,說明該使用者是否提供了正確的使用者名稱-密碼組合。
以API為中心的網路應用的另外一個特徵就是API一直是無狀態的,這意味著這種應用無法辨別由會話發起的API呼叫。由於API呼叫通常由後端程式碼構成,實現對會話的掌控將比較困難,因為這其中通常沒有cookies介入。這種侷限事實上是好事——它“迫使”開發者建造不基於當前使用者狀態工作的API,但是相應地在功能上,它使測試易於進行,因為使用者的當前狀態無需被重建。
為什麼要經歷這些麻煩?
作為Web開發者,我們已經親眼目睹了技術的進步。有一個常識是,當代的人們不會只通過瀏覽器來使用應用,還會通過其它諸如行動電話和平板電腦之類的裝置使用。舉個例子,這篇發表在Mashable上的名為“使用者在移動應用上花的時間比在網路上的多”的寫道:
一項新近的報告表明,使用者花在移動應用上的時間首次超過了花在網路上的時間。
Flurry對比了其移動資料與來自comScore和Alexa的統計資料,發現在六月,使用者每天花費81分鐘使用移動應用,而只花74分鐘用於網上衝浪。
這裡還有一篇來自ReadWriteWeb的更新的文章“在移動裝置上瀏覽網路的人多於使用IE6和IE7的人數總和”:
來自Sitepoint的 瀏覽器趨勢的最新資料表明,在智慧手機上瀏覽Web的人比使用IE6和IE7瀏覽的人更多。這兩件難有起色的老古董多年來一直是Web開發者的噩夢,它們需要各網站儘可能妥當地降格到至少常用瀏覽器所能支援的水平。但是現在時代不同了;2011年十一月中,6.95%的Web活動在移動瀏覽器上發生,而發生在IE6或IE7上的則只有6.49%。
正如我們所見,越來越多的人正通過其它途徑獲得訊息,特別是移動裝置。
這與我建立以API為中心的網路應用有何關係?
這必將會使我們的應用更加有用,因為它可以用在任何你需要的地方。
建立以API為中心的網路應用的主要優勢之一便是它幫助你建立可以用於任何裝置的功能,瀏覽器、行動電話、甚至是桌面應用。你所需要做的就是建立的API能夠使所有這些裝置利用它完成通訊,然後,瞧!你將能夠建造一個集中式應用,它能夠接受來自使用者所使用的任何裝置的輸入並執行相應的功能。
以API為中心的應用的框圖
通過以這種方式建立應用,我們能夠從容地利用不同的人使用不同的媒介這一優勢。這必將使應用更加有用,因為它能用在使用者需要的任何地方。
為了證明我們的觀點,這裡有一篇關於Twitter的重新設計的網站的文章,文章告訴我們他們現在如何利用他們的API來驅動Twitter.com的,實質上是使其以API為中心:
最重要的架構改動之一就是Twitter.com現在是我們自己API的客戶。它從終端提取資料,此終端與移動網站,我們為iPhone、iPad、Android,以及所有第三方應用所用端點相同。這一轉變使我們能向API團隊分配更多的資源,同時生成了40多個補丁。在初始頁面負載和來自客戶端的每個呼叫上,所有的資料現在都是從一個高度優化的JSON段快取中獲取的。
在本篇教程中,我們將建立一個簡單的TODO列表應用,該應用以API為中心;還要建立一個瀏覽器上的前端客戶端,該客戶端與我們的TODO列表應用進行互動。文末,你就能瞭解一個以API為中心的應用的有機組成部分,同時,還能瞭解怎樣使應用和客戶端兩者之間的安全通訊變得容易。記住這些,我們開始吧!
步驟 1: 規劃該應用的功能
本教程中我們將要構建的這個 TODO 應用將會有下面幾個基本的CRUD功能:
- 建立 TODO 條目
- 讀取 TODO 條目
- 更新 TODO 條目 (重新命名,標記為完成,標記為未完成)
- 刪除 TODO 條目
每一個 TODO 條目將擁有:
- 一個標題 Title
- 一個截止日期 Date Due
- 一個描述 Description
- 一個判斷 TODO 條目是否完成的標誌 Is Done
讓我們模擬一下該應用,使我們考慮該應用以後會是什麼樣子時,能有有一個直觀的參考:
簡單的TODO 模擬示例
步驟 2: 建立API伺服器
既然我們是在開發一個以API為中心的應用,我們將建立兩個“專案”: API 伺服器,和前端客戶端。 我們首先從建立API伺服器開始。
在你的web server資料夾,建立一個資料夾,命名為simpletodo_api,然後建立一個index.php檔案。這個index.php檔案將作為一個訪問API的前端控制器,所以,所有訪問API伺服器的請求都會由該檔案產生。開啟它並往裡輸入下列程式碼:
<?php // 定義資料目錄的路徑 define('DATA_PATH', realpath(dirname(__FILE__).'/data')); //引入我們的models include_once 'models/TodoItem.php'; //在一個try-catch塊中包含所有程式碼,來捕獲所有可能的異常! try { //獲得在POST/GET request中的所有引數 $params = $_REQUEST; //獲取controller並把它正確的格式化使得第一個字母總是大寫的 $controller = ucfirst(strtolower($params['controller'])); //獲取action並把它正確的格式化,使它所有的字母都是小寫的,並追加一個'Action' $action = strtolower($params['action']).'Action'; //檢查controller是否存在。如果不存在,丟擲異常 if( file_exists("controllers/{$controller}.php") ) { include_once "controllers/{$controller}.php"; } else { throw new Exception('Controller is invalid.'); } //建立一個新的controller例項,並把從request中獲取的引數傳給它 $controller = new $controller($params); //檢查controller中是否存在action。如果不存在,丟擲異常。 if( method_exists($controller, $action) === false ) { throw new Exception('Action is invalid.'); } //執行action $result['data'] = $controller->$action(); $result['success'] = true; } catch( Exception $e ) { //捕獲任何一次樣並且報告問題 $result = array(); $result['success'] = false; $result['errormsg'] = $e->getMessage(); } //回顯呼叫API的結果 echo json_encode($result); exit();
實質上,這裡我們建立的是一個簡單的前端控制器,它實現了下列功能:
- 接受一次擁有任意個引數的API呼叫
- 為本次API呼叫抽取出Controller和Action
- 進行必要的檢查確保Controller和Action都存在
- 執行API呼叫
- 捕獲異常,如果有的話
- 返回一個結果給呼叫者
除了需要建立index.php外你還需要建立三個資料夾: controllers, models 和 data.
- controllers 資料夾存放的是所有我們API伺服器將會用到的的控制器。我們用MVC架構來使API伺服器結構更清楚合理。
- models 資料夾存放所有API伺服器要用到的資料模型。
- data 資料夾將會用來儲存API伺服器的任何資料。
在controllers資料夾下建立一個叫Todo.php的檔案。這將是任何TODO列表有關任務的控制器。按照TODO應用所需提供的功能,向Todo控制器裡面新增必要的方法:
<?php class Todo { private $_params; public function __construct($params) { $this->_params = $params; } public function createAction() { //create a new todo item } public function readAction() { //read all the todo items } public function updateAction() { //update a todo item } public function deleteAction() { //delete a todo item } }
現在為每個action中新增必要的功能實現。我將會提供createAction()方法的原始碼,其他方法將留作作業。如果你覺得毫無頭緒,你也可以下載示例的原始碼,從那裡拷貝。
public function createAction() { //create a new todo item $todo = new TodoItem(); $todo->title = $this->_params['title']; $todo->description = $this->_params['description']; $todo->due_date = $this->_params['due_date']; $todo->is_done = 'false'; //pass the user's username and password to authenticate the user $todo->save($this->_params['username'], $this->_params['userpass']); //return the todo item in array format return $todo->toArray(); }
在資料夾models下建立TodoItem.php,這樣我們就可以建立“條目新增”的程式碼了。注意:我並沒有和資料庫進行連線,相反我將資訊儲存到檔案中,雖然這可以用任何資料庫來實現,但是 這樣做相對來說要容易些。
<?php class TodoItem { public $todo_id; public $title; public $description; public $due_date; public $is_done; public function save($username, $userpass) { //get the username/password hash $userhash = sha1("{$username}_{$userpass}"); if( is_dir(DATA_PATH."/{$userhash}") === false ) { mkdir(DATA_PATH."/{$userhash}"); } //if the $todo_id isn't set yet, it means we need to create a new todo item if( is_null($this->todo_id) || !is_numeric($this->todo_id) ) { //the todo id is the current time $this->todo_id = time(); } //get the array version of this todo item $todo_item_array = $this->toArray(); //save the serialized array version into a file $success = file_put_contents(DATA_PATH."/{$userhash}/{$this->todo_id}.txt", serialize($todo_item_array)); //if saving was not successful, throw an exception if( $success === false ) { throw new Exception('Failed to save todo item'); } //return the array version return $todo_item_array; } public function toArray() { //return an array version of the todo item return array( 'todo_id' => $this->todo_id, 'title' => $this->title, 'description' => $this->description, 'due_date' => $this->due_date, 'is_done' => $this->is_done ); } }
createAction方法使用到TodoItem模型裡面兩個方法:
- save() – 該方法將TodoItem儲存到一個檔案中,如有必要,需要設定todo_id。
- toArray() – 該方法返回一個以變數為索引的陣列Todo條目。
由於API需要通過HTTP請求呼叫,在瀏覽器輸入如下地址測試API:
如果沒有錯,你應該在data資料夾下看到一個新的資料夾,在該資料夾裡面有一個檔案,檔案內容如下:
createAction()結果
恭喜!您已經成功建立了一個的API伺服器和API呼叫!
步驟3:確保API伺服器具有APP ID和APP SECRET
目前,API伺服器被設定為接受全部API請求。我們將需要將之限制在我們自己的應用上,以確保只有我們自己的前端客戶端能夠完成API請求。另外,你實際上也可以建立一個系統,其中的使用者可以建立他們自己的應用,而那些應用也用用對你的API伺服器的訪問權,這與Facebook和Twitter的應用的的工作原理類似。
我們從為使用API伺服器的使用者建立一組id-密碼對開始。由於這只是一個Demo,我們可以使用任何隨機的、32位字串。對於APP ID,我們將其設定為APP001。
再次開啟index.php檔案,然後用下列程式碼更新之:
<?php // Define path to data folder define('DATA_PATH', realpath(dirname(__FILE__).'/data')); //Define our id-key pairs $applications = array( 'APP001' => '28e336ac6c9423d946ba02d19c6a2632', //randomly generated app key ); //include our models include_once 'models/TodoItem.php'; //wrap the whole thing in a try-catch block to catch any wayward exceptions! try { //*UPDATED* //get the encrypted request $enc_request = $_REQUEST['enc_request']; //get the provided app id $app_id = $_REQUEST['app_id']; //check first if the app id exists in the list of applications if( !isset($applications[$app_id]) ) { throw new Exception('Application does not exist!'); } //decrypt the request $params = json_decode(trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $applications[$app_id], base64_decode($enc_request), MCRYPT_MODE_ECB))); //check if the request is valid by checking if it's an array and looking for the controller and action if( $params == false || isset($params->controller) == false || isset($params->action) == false ) { throw new Exception('Request is not valid'); } //cast it into an array $params = (array) $params; ... ... ...
我們在這裡已經完成的實際上是實現一個非常簡單的認證我們的前端客戶端的方法,它利用了與公共-私有金鑰認證相似的系統。基本上,這裡給出的就是認證過程的步驟分解:
公鑰加密
- 完成一個API呼叫,其中提供了$app_id和$enc_request
- $enc_request的值是API呼叫的引數,利用APP KEY進行加密。APP KEY絕對不會被髮送到伺服器,它只是被用來雜湊請求。此外,該請求只能利用APP KEY被解密
- 一旦API呼叫到達API伺服器,它會查驗它自己的應用列表是否與APP ID所提供的一致
- 當呼叫被發現,API伺服器嘗試利用與APP ID傳送的金鑰相匹配的金鑰進行解密
- 如果請求被解密成功,那麼繼續執行程式
既然API伺服器已經確保具有APP ID和APP SECRET,那麼我們就可以開始編寫使用API伺服器的前端客戶端了。
步驟4:建立瀏覽器客戶端
我們從為前端客戶端設定新建資料夾開始。在你的Web伺服器上的資料夾中建立一個名為simpletodo_client_browser的資料夾。完成後,建立一個index.php檔案,並將下列程式碼寫進去:
<!DOCTYPE html> <html> <head> <title>SimpleTODO</title> <link rel="stylesheet" href="css/reset.css" type="text/css" /> <link rel="stylesheet" href="css/bootstrap.min.css" type="text/css" /> <script src="js/jquery.min.js"></script> <script src="js/jquery-ui-1.8.16.custom.min.js"></script> <style> body { padding-top: 40px; } #main { margin-top: 80px; text-align: center; } </style> </head> <body> <div class="topbar"> <div class="fill"> <div class="container"> <a class="brand" href="index.php">SimpleTODO</a> </div> </div> </div> <div id="main" class="container"> <form class="form-stacked" method="POST" action="login.php"> <div class="row"> <div class="span5 offset5"> <label for="login_username">Username:</label> <input type="text" id="login_username" name="login_username" placeholder="username" /> <label for="login_password">Password:</label> <input type="password" id="login_password" name="login_password" placeholder="password" /> </div> </div> <div class="actions"> <button type="submit" name="login_submit" class="btn primary large">Login or Register</button> </div> </form> </div> </body> </html>
這段程式碼的執行結果看起來就像這樣:
SimpleTODO的登入頁
需要注意的是我在這裡已經包含了兩個JavaScript檔案和兩個CSS檔案:
- reset.css是你的標準CSS重置指令碼。我使用了meyerweb.com css reset.
- bootstrap.min.css是Twitter Bootstrap
- jquery.min.js是最新版的jQuery library
- jquery-ui-1.8.16.custom.min.js是最新版的jQuery UI library
接下來,我們建立login.php檔案來儲存客戶端會話中的使用者名稱和密碼。
<?php //get the form values $username = $_POST['login_username']; $userpass = $_POST['login_password']; session_start(); $_SESSION['username'] = $username; $_SESSION['userpass'] = $userpass; header('Location: todo.php'); exit();
這裡,我們簡單地為使用者開啟一次會話,所依據的是使用者所提供的使用者名稱和密碼組合。這充當了簡單的組合金鑰,它允許使用者訪問某個特定使用者名稱和密碼組合所儲存的TODO項。然後我們重定向至todo.php,那裡是我們開始與API伺服器互動的地方。然而在我們開始編寫todo.php檔案之前,先建立一個 ApiCaller類,它將封裝我們所需的全部API呼叫方法,包括請求的加密。
建立apicaller.php,並把下面的程式碼寫進去:
<?php class ApiCaller { //some variables for the object private $_app_id; private $_app_key; private $_api_url; //construct an ApiCaller object, taking an //APP ID, APP KEY and API URL parameter public function __construct($app_id, $app_key, $api_url) { $this->_app_id = $app_id; $this->_app_key = $app_key; $this->_api_url = $api_url; } //send the request to the API server //also encrypts the request, then checks //if the results are valid public function sendRequest($request_params) { //encrypt the request parameters $enc_request = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->_app_key, json_encode($request_params), MCRYPT_MODE_ECB)); //create the params array, which will //be the POST parameters $params = array(); $params['enc_request'] = $enc_request; $params['app_id'] = $this->_app_id; //initialize and setup the curl handler $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->_api_url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, count($params)); curl_setopt($ch, CURLOPT_POSTFIELDS, $params); //execute the request $result = curl_exec($ch); //json_decode the result $result = @json_decode($result); //check if we're able to json_decode the result correctly if( $result == false || isset($result['success']) == false ) { throw new Exception('Request was not correct'); } //if there was an error in the request, throw an exception if( $result['success'] == false ) { throw new Exception($result['errormsg']); } //if everything went great, return the data return $result['data']; } }
我們將利用ApiCaller類向我們的API伺服器傳送請求。這樣,所有必需的加密和cURL初始化程式碼將會寫在一個地方,我們就不用重複程式碼了。
- __construct函式接受三個引數:
- $app_id——客戶端的APP ID(瀏覽器端是APP001)
- $app_key——客戶端的APP KEY(瀏覽器端是28e336ac6c9423d946ba02d19c6a2632)
- $api_url——API伺服器的URL,此處為http://localhost/simpletodo_api/
- sendRequest()函式:
- 利用mcrypt庫以API服務對其解密的同樣方式來對請求引數進行加密
- 生成發往API伺服器的$_POST引數
- 通過cURL執行API呼叫
- 查驗API呼叫的結果是否正確
- 當一切都按計劃進行的時候返回資料
現在,我們開始寫todo.php。首先,我們建立一些程式碼來為密碼為test1234的使用者nikko(這是我們先前用來測試API伺服器的那個使用者名稱/密碼組合)獲取當前的todo項。
<?php session_start(); include_once 'apicaller.php'; $apicaller = new ApiCaller('APP001', '28e336ac6c9423d946ba02d19c6a2632', 'http://localhost/simpletodo_api/'); $todo_items = $apicaller->sendRequest(array( 'controller' => 'todo', 'action' => 'read', 'username' => $_SESSION['username'], 'userpass' => $_SESSION['userpass'] )); echo ''; var_dump($todo_items);
開啟index.php,以nikko/tes1234登入,然後你應該看到我們先前建立的TODO項的avar_dump()。
恭喜,你已經成功地做好了一個向API伺服器的API呼叫!在這段程式碼中,我們已經:
- 開啟會話,使我們擁有了對$_SESSION中的username以及userpass的訪問權
- 例項化了一個新的ApiCaller類,為其提供了APP ID,APP KEY,以及API伺服器的URL
- 通過sendRequest()方法傳送了一個請求
現在,我們來重新格式化一下資料,讓它們開起來更好看些。向todo.php中新增下列HTML。別忘了移去var_dump()!
<!DOCTYPE html> <html> <head> <title>SimpleTODO</title> <link rel="stylesheet" href="css/reset.css" type="text/css" /> <link rel="stylesheet" href="css/bootstrap.min.css" type="text/css" /> <link rel="stylesheet" href="css/flick/jquery-ui-1.8.16.custom.css" type="text/css" /> <script src="js/jquery.min.js"></script> <script src="js/jquery-ui-1.8.16.custom.min.js"></script> <style> body { padding-top: 40px; } #main { margin-top: 80px; } .textalignright { text-align: right; } .marginbottom10 { margin-bottom: 10px; } #newtodo_window { text-align: left; display: none; } </style> <script> $(document).ready(function() { $("#todolist").accordion({ collapsible: true }); $(".datepicker").datepicker(); $('#newtodo_window').dialog({ autoOpen: false, height: 'auto', width: 'auto', modal: true }); $('#newtodo').click(function() { $('#newtodo_window').dialog('open'); }); }); </script> </head> <body> <div class="topbar"> <div class="fill"> <div class="container"> <a class="brand" href="index.php">SimpleTODO</a> </div> </div> </div> <div id="main" class="container"> <div class="textalignright marginbottom10"> <span id="newtodo" class="btn info">Create a new TODO item</span> <div id="newtodo_window" title="Create a new TODO item"> <form method="POST" action="new_todo.php"> <p>Title:<br /><input type="text" class="title" name="title" placeholder="TODO title" /></p> <p>Date Due:<br /><input type="text" class="datepicker" name="due_date" placeholder="MM/DD/YYYY" /></p> <p>Description:<br /><textarea class="description" name="description"></textarea></p> <div class="actions"> <input type="submit" value="Create" name="new_submit" class="btn primary" /> </div> </form> </div> </div> <div id="todolist"> <?php foreach($todo_items as $todo): ?> <h3><a href="#"><?php echo $todo->title; ?></a></h3> <div> <form method="POST" action="update_todo.php"> <div class="textalignright"> <a href="delete_todo.php?todo_id=<?php echo $todo->todo_id; ?>">Delete</a> </div> <div> <p>Date Due:<br /><input type="text" id="datepicker_<?php echo $todo->todo_id; ?>" class="datepicker" name="due_date" value="12/09/2011" /></p> <p>Description:<br /><textarea class="span8" id="description_<?php echo $todo->todo_id; ?>" class="description" name="description"><?php echo $todo->description; ?></textarea></p> </div> <div class="textalignright"> <?php if( $todo->is_done == 'false' ): ?> <input type="hidden" value="false" name="is_done" /> <input type="submit" class="btn" value="Mark as Done?" name="markasdone_button" /> <?php else: ?> <input type="hidden" value="true" name="is_done" /> <input type="button" class="btn success" value="Done!" name="done_button" /> <?php endif; ?> <input type="hidden" value="<?php echo $todo->todo_id; ?>" name="todo_id" /> <input type="hidden" value="<?php echo $todo->title; ?>" name="title" /> <input type="submit" class="btn primary" value="Save Changes" name="update_button" /> </div> </form> </div> <?php endforeach; ?> </div> </div> </body> </html>
這段程式碼的執行結果如下:
很酷哈?但它目前啥也幹不了,那麼讓我們開始新增一些功能吧。我將為new_todo.php提供程式碼,它們呼叫todo/createAPI呼叫來建立新的TODO項。建立其他頁(update_todo.php和delete_todo.php)應該與此十分相似,因此我把它們留給你。開啟new_todo.php,然後把下面的程式碼添進去:
<?php session_start(); include_once 'apicaller.php'; $apicaller = new ApiCaller('APP001', '28e336ac6c9423d946ba02d19c6a2632', 'http://localhost/simpletodo_api/'); $new_item = $apicaller->sendRequest(array( 'controller' => 'todo', 'action' => 'create', 'title' => $_POST['title'], 'due_date' => $_POST['due_date'], 'description' => $_POST['description'], 'username' => $_SESSION['username'], 'userpass' => $_SESSION['userpass'] )); header('Location: todo.php'); exit(); ?>
正如你所看到的,new_todo.php頁再次使用了ApiCaller呼叫來簡化向API伺服器所傳送的 todo/create請求。這主要完成了與之前相同的事情:
- 開啟一個會話,以使其獲得對儲存於$_SESSION中的$username和$userpass的訪問權
- 例項化一個新的ApiCaller類,為它提供APP ID, APP KEY,以及API伺服器的URL
- 通過sendRequest()方法傳送請求
- 重定向回todo.php
恭喜,它好用了!你已經成功地建立了一個以API為中心的應用!
結論
圍繞API建立並開發應用具有如此之多的優勢。想建立一個Android版的SimpleTODO?你需要的所有功能都已經在API伺服器上了,所以你所要做的就是建立客戶端!想重構或者優化某些類?沒問題——只要確保輸出相同即可。想新增更多的功能?你可以在不影響任何客戶端程式碼的前提下做到!
儘管存在著某些像是更長的開發時間或者更加複雜,但是以這種方式開發網路應用的優勢卻遠比其劣勢更重要。今天的這種開發由我們自己權衡取捨,從而使我們能夠在將來獲益。
你是準備使用一臺API伺服器作為你的下一個Web應用,還是已經在過去的專案中使用了相同的技術?請在評論中告知!
相關文章
- 以應用為中心的企業混合雲管理
- 阿里張磊:如何構建以應用為中心的“Kubernetes”?阿里
- Gartner:85%的企業支援以產品為中心的應用交付模式模式
- 青雲QingCloud推出以應用為中心的全新雲服務體系GCCloud
- 以Web Host的方式來寄宿Web APIWebAPI
- 從以專案為中心轉向以組織為中心(轉)
- 解讀容器 2019:把“以應用為中心”進行到底
- 阿里張磊:如何構建以應用為中心的“Kubernetes”?(內含 QA 整理)阿里
- 以Self Host的方式來寄宿Web APIWebAPI
- 以api文件為中心--前後端分開發離新思維API後端
- 用Web API Client 呼叫 Web APIWebAPIclient
- 以資料庫為中心的架構與以領域為中心的架構的區別 - DevSDhami資料庫架構dev
- ArgoCD + KubeVela:以開發者為中心的 GitOpsGoGit
- 以資料為中心的網路安全
- Maven建立Web應用程式專案MavenWeb
- EEA為以太坊以隱私為主的Web應用釋出標準化架構棧Web架構
- IoC在ASP.NET Web API中的應用ASP.NETWebAPI
- 【ASP.NET Web API教程】2 建立各種Web APIASP.NETWebAPI
- 6個建立Web應用程式的高效PHP框架WebPHP框架
- 為 UWP 應用提供的 .NET 網路 APIAPI
- rita:利用 NATS 實現以事件為中心和反應模式的工具包事件模式
- 建立索引的原則-以innodb為例索引
- “以資料為中心”的數安實踐感悟
- 以客戶端為中心的錯誤處理客戶端
- CRM實現以客戶為中心 (轉)
- 如何為 Flask Web 應用配置 NginxFlaskWebNginx
- 如何用TypeScript來建立一個簡單的Web應用TypeScriptWeb
- 以Hub為中心節點的網路技術探析
- 波士頓諮詢:工作的未來——以人才為中心
- 在Web應用中動態建立PDF檔案Web
- Gradle入門(6):建立Web應用專案GradleWeb
- Microsoft 365應用將取代Office應用,成為體驗微軟服務的新中心ROS微軟
- 建立第一個ArcGIS API for Silverlight應用API
- 用TAPI 3.0 建立呼叫中心 (轉)API
- WEB應用是如何運用Spring的?#①Spring的IOC容器如何在WEB中建立?WebSpring
- 為你的Go應用建立輕量級Docker映象?GoDocker
- 為你的 Linux 應用建立 .desktop 檔案Linux
- 美創科技以資料為中心的安全治理實踐