日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

使用PHP创建一个REST API(译)

發(fā)布時間:2024/10/12 php 64 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用PHP创建一个REST API(译) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

最近API在網(wǎng)絡(luò)領(lǐng)域有些風靡,明確的說是REST的影響力。這實在沒什么好驚訝的,因為在任何編程語言中,消費REST API都是非常的容易。構(gòu)建它也非常的簡單,因為本質(zhì)上你不會用到任何那些已存在很久的HTTP細則。由于Rails對REST做出的深思熟慮的支持,包括提供和消費這些API(這已經(jīng)被所有那些和我共事的Rails狂熱者闡述過),我要贊美Rails,這樣的事情并不常發(fā)生。

?

說真的,如果你從未使用過REST,但是使用過(或者更糟糕的,構(gòu)建過)SOAP API,或僅僅開過一個WSDL并且將你報價單的頭部分解過,伙計,我能有好消息告訴你嗎。

?

那么,REST到底是什么?為什么你應(yīng)該關(guān)注它?

在開始寫代碼前,我想要確保每個人對REST是什么以及它如何對API有利已經(jīng)有了較好的認識。首先,從技術(shù)上來說,REST并不僅僅針對API,它更像一個通用的概念。然而明顯的是,在這篇文章中,我們將在API的語境下談?wù)揜EST。所以,讓我們來看看API的基本需求以及REST如何作用它們。

?

請求

所有的API都需要接受請求。有代表性的,對于一個RESTful API,你會擁有一種定義良好的URL模式。讓我們假設(shè)你想要在你的網(wǎng)站上為用戶提供一個API(我知道,我總是為我的例子使用“用戶”的概念)。好的,你的URL結(jié)構(gòu)可能類似于“api/users”和“api/users/[id]”這樣,這取決于針對你的API被請求的操作類型。你還需要考慮要如何接受數(shù)據(jù)。目前大多數(shù)人正在使用JSON或XML,我個人更傾向于JSON,因為它與JavaScript配合使用更好些,而且PHP擁有簡單的功能來編碼和解碼JSON。如果過去你希望你的API真正的穩(wěn)健,你能夠接受兩者通過嗅探出請求的內(nèi)容類型(例如:application/json 或 application/xml),但是更讓人接受的是將內(nèi)容類型限制成一種。真見鬼,如果你愿意你甚至可以使用簡單的鍵/值對。

?

請求的另一塊內(nèi)容是它實際上做了什么,比如加載,保存等等。一般的,你不得不想出某種體系架構(gòu)來定義請求者(消費者)請求的是什么動作,但是REST簡化了這些。通過使用HTTP請求方法或動詞,我們不需要定義任何東西。我們能夠僅僅使用GET,POST,PUT和DELETE方法,這些包含了任何我們需要的請求。你可以將這些動詞等價于標準的CRUD風格的東西:GET=加載/檢索,POST=創(chuàng)建,PUT=更新,DELETE=delete。注意到這些動詞不可以直接對應(yīng)CRUD是重要的,但是這是一種理解它們的好方法。重新回到上面URL的例子,讓我們看一看一些可能的請求意味著什么:

  • GET request to /api/users – 列出所有用戶
  • GET request to /api/users/1 – 列出ID為1的用戶信息
  • POST request to /api/users – 創(chuàng)建一個新用戶
  • PUT request to /api/users/1 – 更新ID為1的用戶信息
  • DELETE request to /api/users/1 – 刪除ID為1的用戶信息

正如你希望看到的一樣,REST通過一些簡單,易于理解的標準和協(xié)議已經(jīng)處理了很多在構(gòu)建API時的主要的棘手問題,但對于一個良好的API還有另一塊內(nèi)容。

?

響應(yīng)

因此,REST處理請求非常的容易,但它也容易生成響應(yīng)。類似于請求,一個RESTful響應(yīng)有兩個主要的部件:響應(yīng)主體和狀態(tài)碼。響應(yīng)主體非常容易去處理。像請求一樣,大多數(shù)REST上的響應(yīng)或者是JSON文件或者是XML文件(可能在POST情況下僅僅是一個平面文件,這個我們將在之后提到)。同樣的,像請求一樣,通過另一部分HTTP請求細則—“Accept”,用戶能夠指定他們想要的響應(yīng)類型。如果用戶希望得到一個XML響應(yīng),他們可以僅僅發(fā)送一個Accept頭信息“Accept: application/xml”作為請求的一部分。無可否認,這個方法沒有被廣泛的采用,所以你也能在URL中使用擴展的概念。例如,“/api/users.xml”意味著用戶想要XML作為響應(yīng),類似的,“ /api/users.json”意味著用戶要響應(yīng)JSON(“/api/users/1.json/xml”同理)。無論你選擇哪種方式,你都應(yīng)該選擇一種默認的響應(yīng)類型,因為大多數(shù)情況人們甚至都不會告訴你他們想要的。再次聲明,我會說選擇JSON。如此,沒有Accept頭或擴展(例如:/api/users)也不應(yīng)該失敗,它應(yīng)該僅僅以默認的響應(yīng)類型做“容錯”處理。

?

但是,錯誤和另外一些重要的與請求相關(guān)聯(lián)的狀態(tài)信息怎么辦呢?這簡單,使用HTTP狀態(tài)碼!這已經(jīng)超過了我對于構(gòu)建RESTful API的興趣。通過使用HTTP狀態(tài)碼,你不需要為你的API想出一種“錯誤/成功”處理模式,這已經(jīng)被實現(xiàn)了。例如,如果一個用戶用POST方法發(fā)送“ /api/users”的請求,并且你想要返回一個成功的產(chǎn)物,簡單的發(fā)送一個201狀態(tài)碼(201=被創(chuàng)造)就可以了。如果失敗,發(fā)送500狀態(tài)碼(500=內(nèi)部服務(wù)器錯誤),或者如果搞砸了發(fā)送400狀態(tài)碼(400=錯誤請求)。可能用戶嘗試用一些不被接受的post去攻擊API端點,發(fā)送一個501狀態(tài)碼(不被執(zhí)行)。或許你的MySQL服務(wù)器宕機了,因此你的API會被臨時性的凍結(jié),發(fā)送一個503狀態(tài)碼(服務(wù)器不可用)。希望你理解了這個思路。如果你想要關(guān)于狀態(tài)碼的內(nèi)容,在wikipedia上查閱它們:List of HTTP Status Codes。

?

我希望你了解了通過使用REST的概念構(gòu)建你的API的所有優(yōu)勢。這真的非常的酷,這沒有在PHP社區(qū)被廣泛的討論是一件恥辱的事(至少就我所討論到的)。我認為很大部分原因是缺乏關(guān)于如何去處理GET或POST以外,也就是PUT和DELETE方法請求的好的文檔。不可否認,處理它們確實有些蠢,但是這肯定不困難。我非常確認一些流行的框架里面很可能存在某種形式的REST實現(xiàn),但我并不是一個狂熱的框架迷(基于很多的原因以致于我不想加入),即使有人已經(jīng)為你實現(xiàn)了解決方案,知道這些對你也是有好處的。

如果你仍然不確信這是一種有用的API范型,看看REST為RoR做了些什么。主要被標榜的一條是構(gòu)建API將會如何的簡單(通過一些RoR狂熱者,我確信),事實上也確實如此。對于RoR我知之甚少,但是辦公室周圍的那些RoR迷多次的給我指出這點。但是,好吧我離題了,讓我們寫一些代碼!

?

開始使用REST和PHP

一條最終的免責聲明:我們將要看到的代碼是不可能作為一種穩(wěn)健的解決方案的例子的。在這里,我的主要目的是向你展示如何在PHP中處理REST的獨立部件,將構(gòu)建最終解決方案的權(quán)利留給你。

讓我們向深處挖掘!我認為對于我們需要創(chuàng)建一個REST API這件事最好的做一些實際有用的事就是創(chuàng)建一個類,這個類將提供所有的工具函數(shù)。我們也會創(chuàng)建一個小類用來儲存我們的數(shù)據(jù)。你也可以拿走它擴展它和在自己的需求中使用它。讓我們貼一些代碼:

1 class RestUtils 2 { 3 public static function processRequest() 4 { 5 6 } 7 8 public static function sendResponse($status = 200, $body = '', $content_type = 'text/html') 9 { 10 11 } 12 13 public static function getStatusCodeMessage($status) 14 { 15 // these could be stored in a .ini file and loaded 16 // via parse_ini_file()... however, this will suffice 17 // for an example 18 $codes = Array( 19 100 => 'Continue', 20 101 => 'Switching Protocols', 21 200 => 'OK', 22 201 => 'Created', 23 202 => 'Accepted', 24 203 => 'Non-Authoritative Information', 25 204 => 'No Content', 26 205 => 'Reset Content', 27 206 => 'Partial Content', 28 300 => 'Multiple Choices', 29 301 => 'Moved Permanently', 30 302 => 'Found', 31 303 => 'See Other', 32 304 => 'Not Modified', 33 305 => 'Use Proxy', 34 306 => '(Unused)', 35 307 => 'Temporary Redirect', 36 400 => 'Bad Request', 37 401 => 'Unauthorized', 38 402 => 'Payment Required', 39 403 => 'Forbidden', 40 404 => 'Not Found', 41 405 => 'Method Not Allowed', 42 406 => 'Not Acceptable', 43 407 => 'Proxy Authentication Required', 44 408 => 'Request Timeout', 45 409 => 'Conflict', 46 410 => 'Gone', 47 411 => 'Length Required', 48 412 => 'Precondition Failed', 49 413 => 'Request Entity Too Large', 50 414 => 'Request-URI Too Long', 51 415 => 'Unsupported Media Type', 52 416 => 'Requested Range Not Satisfiable', 53 417 => 'Expectation Failed', 54 500 => 'Internal Server Error', 55 501 => 'Not Implemented', 56 502 => 'Bad Gateway', 57 503 => 'Service Unavailable', 58 504 => 'Gateway Timeout', 59 505 => 'HTTP Version Not Supported' 60 ); 61 62 return (isset($codes[$status])) ? $codes[$status] : ''; 63 } 64 } 65 66 class RestRequest 67 { 68 private $request_vars; 69 private $data; 70 private $http_accept; 71 private $method; 72 73 public function __construct() 74 { 75 $this->request_vars = array(); 76 $this->data = ''; 77 $this->http_accept = (strpos($_SERVER['HTTP_ACCEPT'], 'json')) ? 'json' : 'xml'; 78 $this->method = 'get'; 79 } 80 81 public function setData($data) 82 { 83 $this->data = $data; 84 } 85 86 public function setMethod($method) 87 { 88 $this->method = $method; 89 } 90 91 public function setRequestVars($request_vars) 92 { 93 $this->request_vars = $request_vars; 94 } 95 96 public function getData() 97 { 98 return $this->data; 99 } 100 101 public function getMethod() 102 { 103 return $this->method; 104 } 105 106 public function getHttpAccept() 107 { 108 return $this->http_accept; 109 } 110 111 public function getRequestVars() 112 { 113 return $this->request_vars; 114 } 115 }

好的,我們已經(jīng)得到了一個用來保存一些關(guān)于請求(REST請求)信息的簡單類,我們可以利用這個類中的一些靜態(tài)方法去處理請求和響應(yīng)。正如你看到的,我們確實僅有兩個方法要寫。這是整件事情最美的地方!好的,讓我們繼續(xù)。

?

處理請求

處理請求是相當直接的,但是在這里我們能遇到一些獵物(即:PUT和DELETE等,多數(shù)是PUT)。我們將在某個時刻重溫他們,但現(xiàn)在讓我們檢查下RestRequest類。如果你注意到了構(gòu)造函數(shù),你就會看到我們已經(jīng)解釋了HTTP_ACCEPT頭部,如果沒被提供,將默認為JSON。有了這樣的方式,我們只需要處理傳入的數(shù)據(jù)。

?

我們有很多的方式去做這個,但是假設(shè)我們總是會在請求中得到一個鍵值對:‘數(shù)據(jù)’=> 實際的數(shù)據(jù)。同樣假設(shè)實際的數(shù)據(jù)是JSON。在我之前對REST的解釋中,你能夠看到請求的內(nèi)容類型和或者JSON或者XML的處理方式,但是現(xiàn)在我們應(yīng)該盡量讓其簡單。因此,我們處理請求的方法最終看起來像這樣:

1 public static function processRequest() 2 { 3 // get our verb 4 $request_method = strtolower($_SERVER['REQUEST_METHOD']); 5 $return_obj = new RestRequest(); 6 // we'll store our data here 7 $data = array(); 8 9 switch ($request_method) 10 { 11 // gets are easy... 12 case 'get': 13 $data = $_GET; 14 break; 15 // so are posts 16 case 'post': 17 $data = $_POST; 18 break; 19 // here's the tricky bit... 20 case 'put': 21 // basically, we read a string from PHP's special input location, 22 // and then parse it out into an array via parse_str... per the PHP docs: 23 // Parses str as if it were the query string passed via a URL and sets 24 // variables in the current scope. 25 parse_str(file_get_contents('php://input'), $put_vars); 26 $data = $put_vars; 27 break; 28 } 29 30 // store the method 31 $return_obj->setMethod($request_method); 32 33 // set the raw data, so we can access it if needed (there may be 34 // other pieces to your requests) 35 $return_obj->setRequestVars($data); 36 37 if(isset($data['data'])) 38 { 39 // translate the JSON to an Object for use however you want 40 $return_obj->setData(json_decode($data['data'])); 41 } 42 return $return_obj; 43 }

就像我說的,非常的直接。然而,有些事情要注意。首先,特別的對于DELETE請求不可以接受數(shù)據(jù),因此我們在switch中沒有對應(yīng)的case。第二點,你會注意到我們儲存了請求變量和解析過的JSON數(shù)據(jù)這兩者。隨著你可能有另外的東西作為你的請求的一部分(一個API鍵或其他什么東西)時這非常有用,這些東西本身并不是真實的數(shù)據(jù)(像用戶的名字,郵箱,等等)。

那么,我們?nèi)绾问褂眠@個呢?讓我們回到用戶例子。假設(shè)你已經(jīng)為用戶將你的請求路由到正確的控制器了,我們會有一些這樣的代碼:

1 $data = RestUtils::processRequest(); 2 3 switch($data->getMethod) 4 { 5 case 'get': 6 // retrieve a list of users 7 break; 8 case 'post': 9 $user = new User(); 10 $user->setFirstName($data->getData()->first_name); // just for example, this should be done cleaner 11 // and so on... 12 $user->save(); 13 break; 14 // etc, etc, etc... 15 }

請不要在真正的應(yīng)用程序中這樣做,這只是一個應(yīng)急的例子。你會想把這個封裝在一個任何東西都已被合適抽象的很好的控制結(jié)構(gòu)里,但是這個應(yīng)該幫助你理解了如何使用這個素材。好吧,我離題了,讓我們進入到發(fā)送響應(yīng)部分。

?

發(fā)送響應(yīng)

現(xiàn)在我們能解釋請求了,讓我們往前到發(fā)送響應(yīng)部分。我們已經(jīng)知道實際需要做的是發(fā)送正確狀態(tài)碼,可能有一些主體(例如,如果是GET請求),但是會有一個重要的捕獲對于那些沒有主體的響應(yīng)。假如某人用一個不存在的用戶請求攻擊我們簡單地用戶API(如:api/user/123)。在這種情況下,發(fā)送404狀態(tài)碼是合適的,但是簡單地在頭部里發(fā)送狀態(tài)碼是不夠的。如果在你的瀏覽器中查看這個頁面,你將會看到空白頁。這是因為Apache(或者其它運行著的Web服務(wù)器)沒有發(fā)送狀態(tài)碼,所以沒有狀態(tài)頁面。我們需要考慮這些當構(gòu)建我們的方法的時候。記住這些,下面是大致的代碼:

public static function sendResponse($status = 200, $body = '', $content_type = 'text/html') { $status_header = 'HTTP/1.1 ' . $status . ' ' . RestUtils::getStatusCodeMessage($status); // set the status header($status_header); // set the content type header('Content-type: ' . $content_type); // pages with body are easy if($body != '') { // send the body echo $body; exit; } // we need to create the body if none is passed else { // create some body messages $message = ''; // this is purely optional, but makes the pages a little nicer to read // for your users. Since you won't likely send a lot of different status codes, // this also shouldn't be too ponderous to maintain switch($status) { case 401: $message = 'You must be authorized to view this page.'; break; case 404: $message = 'The requested URL ' . $_SERVER['REQUEST_URI'] . ' was not found.'; break; case 500: $message = 'The server encountered an error processing your request.'; break; case 501: $message = 'The requested method is not implemented.'; break; } // servers don't always have a signature turned on (this is an apache directive "ServerSignature On") $signature = ($_SERVER['SERVER_SIGNATURE'] == '') ? $_SERVER['SERVER_SOFTWARE'] . ' Server at ' . $_SERVER['SERVER_NAME'] . ' Port ' . $_SERVER['SERVER_PORT'] : $_SERVER['SERVER_SIGNATURE']; // this should be templatized in a real-world solution $body = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <title>' . $status . ' ' . RestUtils::getStatusCodeMessage($status) . '</title> </head> <body> <h1>' . RestUtils::getStatusCodeMessage($status) . '</h1> <p>' . $message . '</p> <hr /> <address>' . $signature . '</address> </body> </html>'; echo $body; exit; } }

這就是了!在技術(shù)上我們有了處理請求和發(fā)送響應(yīng)的一切需要的東西。讓我們更多的談?wù)劄槭裁次覀冃枰粋€標準的或自定義的響應(yīng)主體。對于GET請求來說,這是非常明顯的,我們需要發(fā)送“XML/JSON”內(nèi)容取代狀態(tài)頁面(只要請求是有效的)。然而,還有POST要處理。在你的應(yīng)用程序里,當你創(chuàng)建一個新實體,你可能會通過類似mysql_insert_id()函數(shù)這樣的方法來獲取新實體的ID。如果一個用戶向你的API發(fā)送一個POST請求,他們同樣想要一個新ID。我通常處理這種情況的方法是簡單的發(fā)送一個作為主體的新的ID(伴隨一個201狀態(tài)碼),但是如果你愿意你可以將他們封裝在XML或JSON中。

讓我們稍微來擴展一下我們的簡單應(yīng)用:

1 switch($data->getMethod) 2 { 3 // this is a request for all users, not one in particular 4 case 'get': 5 $user_list = getUserList(); // assume this returns an array 6 7 if($data->getHttpAccept == 'json') 8 { 9 RestUtils::sendResponse(200, json_encode($user_list), 'application/json'); 10 } 11 else if ($data->getHttpAccept == 'xml') 12 { 13 // using the XML_SERIALIZER Pear Package 14 $options = array 15 ( 16 'indent' => ' ', 17 'addDecl' => false, 18 'rootName' => $fc->getAction(), 19 XML_SERIALIZER_OPTION_RETURN_RESULT => true 20 ); 21 $serializer = new XML_Serializer($options); 22 23 RestUtils::sendResponse(200, $serializer->serialize($user_list), 'application/xml'); 24 } 25 26 break; 27 // new user create 28 case 'post': 29 $user = new User(); 30 $user->setFirstName($data->getData()->first_name); // just for example, this should be done cleaner 31 // and so on... 32 $user->save(); 33 34 // just send the new ID as the body 35 RestUtils::sendResponse(201, $user->getId()); 36 break; 37 }

重申一下,這僅僅是一個例子,但是它展示了(我認為,至少是這樣的)實現(xiàn)RESTful所要做出的努力。

?

總結(jié)

這就是了!我非常自信已經(jīng)把一些觀點易于理解的指了出來,因此我愿意接受你如何更進一步的領(lǐng)悟這個材料,而且或許可以正確的實現(xiàn)它。

在現(xiàn)實的MVC應(yīng)用程序中,你或許想做的是為你的加載個別API控制器的API設(shè)置一個控制器。例如,使用上面的原型,我們可能創(chuàng)建一個包含get(),put(),post()和delete()方法的UserRestController控制器。這些方法將會使用工具來處理請求,智能的做一些需要做的事,然后使用工具發(fā)送響應(yīng)。

你也可以更進一步,抽象出你的API控制器和數(shù)據(jù)模型。不用在你的應(yīng)用程序中為每個數(shù)據(jù)模型創(chuàng)建一個控制器,你可以添加一些邏輯到你的API控制器并且首先尋找一個顯示定義的控制器,如果沒找到,則嘗試去尋找一個存在的模型。例如,URL“api/user/1”將會首先查找一個“user”的rest控制器,如果沒找到,再在你的應(yīng)用程序中尋找一個叫做“user”的模型。如果找到了一個,你可以對這些模型寫一些自動化巫師程序來自動化處理所有的請求。

更進一步,你可以創(chuàng)建一個一般的“l(fā)ist-all”方法,其工作原理類似于先前段落的例子。假設(shè)你的url是“api/users”。API控制器將會首先檢查“users”rest控制器,如果沒找到,識別用戶被多元化,解除多元化,然后查找“user”模型。如果發(fā)現(xiàn)一個,加載一個列表的用戶列表并發(fā)出。

最后,你可以同樣簡單的為你的API加上摘要式身份驗證。假設(shè)你只想要合適認證的用戶有訪問你API的權(quán)限,你可以向這樣在你的處理請求的功能在加入一些代碼(借用我的現(xiàn)有應(yīng)用,有一些常量和變量引用沒有被定義在這個片段中)。

1 // figure out if we need to challenge the user 2 if(emptyempty($_SERVER['PHP_AUTH_DIGEST'])) 3 { 4 header('HTTP/1.1 401 Unauthorized'); 5 header('WWW-Authenticate: Digest realm="' . AUTH_REALM . '",qop="auth",nonce="' . uniqid() . '",opaque="' . md5(AUTH_REALM) . '"'); 6 7 // show the error if they hit cancel 8 die(RestControllerLib::error(401, true)); 9 } 10 11 // now, analayze the PHP_AUTH_DIGEST var 12 if(!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || $auth_username != $data['username']) 13 { 14 // show the error due to bad auth 15 die(RestUtils::sendResponse(401)); 16 } 17 18 // so far, everything's good, let's now check the response a bit more... 19 $A1 = md5($data['username'] . ':' . AUTH_REALM . ':' . $auth_pass); 20 $A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']); 21 $valid_response = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . $A2); 22 23 // last check.. 24 if($data['response'] != $valid_response) 25 { 26 die(RestUtils::sendResponse(401)); 27 }

非常酷的原型,是嗎?通過一點點代碼和一些聰明的邏輯,你可以非常快捷的在你的應(yīng)用程序中加入一個全功能的REST API。我并不僅僅是在鼓勵這個概念,我花了半天時間在我的個人框架中實現(xiàn)了它,又花了另一個半天在里面加入了各式各樣的魔法。如果你對我的最終實現(xiàn)感興趣,在評論中注明,我將非常高興地與你分享。如果你有任何愿意分享的酷的點子,同樣也請在評論中寫下它們。如果我足夠喜歡它,我甚至樂意讓您自己創(chuàng)作關(guān)于這一主題的文章。

下次再見。

?

原文鏈接:Create a REST API with PHP

轉(zhuǎn)載于:https://www.cnblogs.com/jiangxiaobo/p/5615645.html

總結(jié)

以上是生活随笔為你收集整理的使用PHP创建一个REST API(译)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。