首页 » 网站推广 » 新浪apiphp技巧_用PHP创建一个REST API

新浪apiphp技巧_用PHP创建一个REST API

duote123 2024-10-31 0

扫一扫用手机浏览

文章目录 [+]

原文地址:http://hmw.iteye.com/blog/1190827

英文原文地址:Create a REST API with PHP

新浪apiphp技巧_用PHP创建一个REST API

感谢RANDY君的翻译

新浪apiphp技巧_用PHP创建一个REST API
(图片来自网络侵删)

文章内容如下:

最近互联网上比较热门的一个名词是APIs(接口),特殊是leverage REST。
不过考虑到REST APIs在任何措辞下都是非常的大略,也就没什么好惊奇的了。
同时,它也是非常的随意马虎创建,你基本只须要利用已经存在多年的HTTP规范就可以。
我认为Rails措辞的为数不多的优点之一便是良好的REST支持,不仅是供应APIs,同时也有很多的客户端支持(我的一些Rails粉丝同事都向我阐明了这一点)。

负责的讲,如果你从来没有利用过REST,却曾经利用过SOAP API,或者只是大略的打开一个令人头大的WSDL文档。
小伙子,我确实要带给你一个好!

那么,究竟什么是REST?为什么你该当关心?

在我们开始写代码之前,我想要确认每个人都可以很好的理解什么是REST以及它是如何特殊适宜APIs的。
首先,从技能上来讲,REST并不是仅仅特定于APIs运用,它更多的是一个通用的观点。
然而,很明显,我们这篇文章所谈论的REST便是在接口运用的环境下。
因此,让我们看看一个API的基本哀求已经REST如何处理他们。

Requests 要求

所有的APIs都须要吸收要求。
对付一个RESTful API,你须要一个定义好的URL规则,我们假定你想要供应一个接口给你网站上的用户(我知道,我总是利用'用户'这个观点来举例)。
你的URL构造可能类似于这样:'api/users'或者'api/users/[id]',这取决于要求接口的操作类型。
同时,你也须要考虑你想要怎么样吸收数据。
比来一段韶光,很多人正在利用JSON或者XML,从我个人来讲,我更加喜好JSON,由于它可以很好的和javascript进行交互操作,同时PHP也可以很大略的通过json_encode和json_decode两个函数来对它进行编码和解码。
如果你希望自己的接口真正强健,你该当通过识别要求的内容类型(比如application/json或者application/xml)同时许可吸收两种格式。
但是,限定只吸收一种类型的数据也是可以很好的被接管。
真见鬼,如果你乐意,你乃至可以利用大略的键/值对。

一个要求的其他部分是它真正要做的事情,比如加载、保存等。
常日来说,你该当供应几种构造来定义要求者(消费者)所希望的操作,但是REST简化了这些。
通过利用HTTP要求方法或者动作,我们不须要去额外定义任何东西。
我们可以仅仅利用GET,POST,PUT和DELETE方法,这些方法涵盖了我们所须要的每一个要求。
你可以把它和标准的增编削查模式对应起来:GET=加载/检索(查,select),POST=创建(增,Create),PUT=更新(改,update),DELETE=删除(DELETE)。
我们要把稳到,这些动词并没有直接翻译成CRUD(增编削查),但是这个理解它们的一个很好的方法。
因此,回到刚才所举的URL的例子,让我们看一下一些可能的要求的含义:

GET request to /api/users – 列举出所有的用户

GET request to /api/users/1 – 列出ID为1的用户信息

POST request to /api/users – 插入一个新的用户

PUT request to /api/users/1 – 更新ID为1的用户信息

DELETE request to /api/users/1 – 删除ID为1的用户

正如你所希望看到的,REST已经办理了很多令人头疼的创建接口的问题,通过一些大略的,随意马虎理解的标准和协议。
但是一个好的接口还要其余一个方面...

Responses 相应

以是,REST可以很大略的处理要求,同时它也可以大略的处理相应。
和要求类似,一个RESTful的相应紧张包括两个紧张部分:相应体和状态码。
相应体非常随意马虎去处理。
就像要求,大部分的REST相应常日是JSON或者XML格式(大概对POST操作来说仅仅是纯文本,我们稍后会谈论),和要求类似,消费者可以通过设置HTTP规范的'Accept'选项来规定自己做希望吸收到的相应数据类型。
如果一个消费者希望吸收到XML相应,他们仅仅须要发送一个包含类似于(”Accept: application/xml”)这样的头信息要求。
不可否认,这种办法并没有被广泛的采取(纵然该当这样),因此你也可以利用URL后缀的形式,例如:/api/users.xml意味开花费者希望得到XML相应,同样,/api/users.json意味着JSON格式的相应(/api/users/1.json/xml也是一样)。
不管你采取哪一种方法,你都须要设定一个默认的相应类型,由于很多时候人们并不会见告你他们希望什么格式。
再次地,我会选择JSON来谈论。
以是,没有Accept头信息或者扩展(例如:/api/users)不应该失落败,而是采取默认的相应类型。

但是和要求有关的缺点和其他主要的状态信息怎么办呢?大略,利用HTTP的状态码!
这是我创建RESTful接口最喜好的事情之一。
通过利用HTTP状态码,你不须要为你的接口想出error/success规则,它已经为你做好。
比如:如果一个消费者提交数据(POST)到/api/users,你须要返回一个成功创建的,此时你可以大略的发送一个201状态码(201=Created)。
如果失落败了,做事器端失落败就发送一个500(500=内部做事器缺点),如果要求中断就发送一个400(400=缺点要求)。
大概他们会考试测验向一个不接管POST要求的接口提交数据,你就可以发送一个501缺点(未实行)。
又或者你的MySQL做事器挂了,接口也会临时性的中断,发送一个503缺点(做事不可用)。
幸运的是,你已经知道了这些,如果你想要理解更多关于状态码的资料,可以在维基百科上查找:List of HTTP Status Codes。

我希望你能看到REST接口的这些优点。
它真的超级酷。
在PHP社区社区里没有被广泛的谈论真是非常的遗憾(至少我知道的是这样)。
我以为这紧张是由于没有很好的文档先容如何处理除了GET和POST之后的要求,即PUT和DELETE。
不可否认,处理这些是有点傻,但是却不难。
我相信一些盛行的框架大概已经有了某种REST的实现办法,但是我不是一个框架粉丝(缘故原由有很多),并且纵然有人已经为你供应理解决方案,你知道这些也是非常有好处的。

如果你还是不太自傲这是一个非常有用的API范式,看一下REST已经为Ruby on Rails做了什么。
个中最令人称道的便是创建接口的便利性(通过某种RoR voodoo,我确信),而且确实如此。
虽然我对RoR理解很少,但是办公室的Ruby粉丝们向我说教过很多次。
不好意思跑题了,让我们开始写代码。

Getting Started with REST and PHP 开始利用PHP写REST

末了一项免责声明:我们接下来供应的代码并不能被用来作为一个稳健的办理方案。
我的紧张目的是向大家展示如果利用PHP处理REST的每个单独部分,而把末了的办理方案留给你们自己去创建。

那么,让我们开始深入代码。
我认为做一个实际事情做好的方法便是新建一个class,这个class将供应创建REST API所须要的所有功能性方法。
现在我们新建一个小的class来存储我们的数据。
你可以把它拿去扩展一下然后运用到自己的需求中。
我们现在开始写点东西:

Php代码

class RestUtils { public static function processRequest{ } public static function sendResponse($status = 200, $body = '', $content_type = 'text/html'){ } public static function getStatusCodeMessage($status){ // these could be stored in a .ini file and loaded // via parse_ini_file... however, this will suffice // for an example // 这些该当被存储在一个.ini的文件中,然后通过parse_ini_file函数来解析出来,然而这样也足够了,比如: $codes = Array( 100 => 'Continue', 101 => 'Switching Protocols', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => '(Unused)', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported' ); return (isset($codes[$status])) ? $codes[$status] : ''; } } class RestRequest { private $request_vars; private $data; private $http_accept; private $method; public function __construct{ $this->request_vars = array; $this->data = ''; $this->http_accept = (strpos($_SERVER['HTTP_ACCEPT'], 'json')) ? 'json' : 'xml'; $this->method = 'get'; } public function setData($data){ $this->data = $data; } public function setMethod($method){ $this->method = $method; } public function setRequestVars($request_vars){ $this->request_vars = $request_vars; } public function getData{ return $this->data; } public function getMethod{ return $this->method; } public function getHttpAccept{ return $this->http_accept; } public function getRequestVars{ return $this->request_vars; } }

好,现在我们有了一个大略的class来存储request的一些信息(RestRequest),和一个供应几个静态方法的class来处理要乞降相应。
就像你能看到的,我们还有两个方法要去写,这才是全体代码的关键所在,让我们连续...

处理要求的过程非常直接,但是这才是我们可以有所收成的地方(即PUT/DELETE,大多数是PUT),我们接下来将会谈论这些。
但是让我们先来检讨一下RestRequest这个class,在布局方法中,你会看到我们已经处理了HTTP_ACCEPT的头信息,并且将JSON作为默认值。
这样,我们就只须要处理传入的数据。

我们有几个方法可以选择,但是让我们假设在要求信息的总是可以吸收到键/值对:'data'=>真实数据。
同时假设真实数据是JSON格式的。
正如我前文所述,你可以根据要求的内容类型来处理JSON或者XML,但是让我们现在大略一点。
那么,我们处理要求的方法将会类似于这样:

Php代码

public static function processRequest{ // get our verb 获取动作 $request_method = strtolower($_SERVER['REQUEST_METHOD']); $return_obj = new RestRequest; // we'll store our data here 在这里存储要求数据 $data = array; switch ($request_method){ // gets are easy... case 'get': $data = $_GET; break; // so are posts case 'post': $data = $_POST; break; // here's the tricky bit... case 'put': // basically, we read a string from PHP's special input location, // and then parse it out into an array via parse_str... per the PHP docs: // Parses str as if it were the query string passed via a URL and sets // variables in the current scope. parse_str(file_get_contents('php://input'), $put_vars); $data = $put_vars; break; } // store the method $return_obj->setMethod($request_method); // set the raw data, so we can access it if needed (there may be // other pieces to your requests) $return_obj->setRequestVars($data); if(isset($data['data'])){ // translate the JSON to an Object for use however you want $return_obj->setData(json_decode($data['data'])); } return $return_obj; }

Like I said, pretty straight-forward. However, a few things to note… First, you typically don’t accept data for DELETE requests, so we don’t have a case for them in the switch. Second, you’ll notice that we store both the request variables, and the parsed JSON data. This is useful as you may have other stuff as a part of your request (say an API key or something) that isn’t truly the data itself (like a new user’s name, email, etc.).

正如我刚才所说的,非常的大略直接高效。
然后,有几点须要把稳:首先,我们不接管DELETE要求,因此我们在switch中不供应相应的case条件。
其次,你会把稳到我们把要求参数和解析后的JSON数据都存储起来了,这在要求中有其他须要处理的数据时会变得非常有用(API key或者其他),这些并不是要求的数据本身(比如一个新用户的名字、电子邮箱等)。

那么,我们如何利用它呢?让我们回到刚才user的例子。
假设你已经通过路由把要求对应到精确的users掌握器,代码如下:

Php代码

$data = RestUtils::processRequest; switch($data->getMethod){ case 'get': // retrieve a list of users break; case 'post': $user = new User; $user->setFirstName($data->getData->first_name); // just for example, this should be done cleaner // and so on... $user->save; break; // etc, etc, etc... }

请不要在真实的运用中这样做,这是一个非常快速和不干净的示例。
你该当利用一个设计良好的掌握构造来把它包裹起来,适当的抽象化,但是这样有助于你理解如何利用这些东西。
让我们连续代码,发送一个相应信息。

既然我们已经可以解析要求,那么接下来我们连续来发送一个相应。
我们已经知道我们真正须要去做的是发送一个精确的状态码和一些相应体(例如这是一个GET要求),但是对付没有体的相应来说有一个主要的catch(译者:不好意思,实在是不知道如何翻译这个词)。
假定某个人向我们的user接口发送一个要求某个用户信息的要求,而这个用户却不存在(比如:api/user/123),此时系统发送最得当的状态码是404。
但是大略的在头信息中发送状态码是不足的,如果你通过网页浏览器浏览该页面,你会看到一个空缺页面。
这是由于apache做事器(或者其他做事器)并不会发送此状态码,因此没有状态页面。
我们须要在构建方法的时候考虑到这一点。
把所有的东西都考虑进去,代码会类似于下面这样:

Php代码

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 = ' 'http://www.w3.org/TR/html4/strict.dtd'> 'Content-Type' content='text/html; charset=iso-8859-1'> ' . $status . ' ' . RestUtils::getStatusCodeMessage($status) . ' ' . RestUtils::getStatusCodeMessage($status) . '' . $message . ' ' . $signature . ' '; echo $body; exit; } }

就这样,从技能上来说,我们已经具备了处理要乞降发送相应的所有东西。
下面我们再谈论以下为什么我们须要一个标准的相应提或者一个自定义的。
对付GET要求来说,非常明显,我们须要发送XML/JSON内容而不是一个状态页(假设要求是合法的)。
然后,我们还有POST要求要去处理。
在你的运用内部,当你创建一个新的实体,你大概须要利用通过类似于mysql_insert_id这样的函数得到这个实体的ID。
那么,当一个用户提交到你的接口,他们将很可能想要知道这个新的ID是什么。
在这种情形下,我常日的做法是非常大略的把这个新ID作为相应的体发送给用户(同时发送一个201的状态码头信息),但是如果你乐意,你也可以利用XML或者JSON来把它包裹起来。

现在,让我们来扩展一下我们的例子,让它更加实际一点:

Php代码

switch($data->getMethod){ // this is a request for all users, not one in particular case 'get': $user_list = getUserList; // assume this returns an array if($data->getHttpAccept == 'json'){ RestUtils::sendResponse(200, json_encode($user_list), 'application/json'); }else if ($data->getHttpAccept == 'xml') { // using the XML_SERIALIZER Pear Package $options = array ( 'indent' => ' ', 'addDecl' => false, 'rootName' => $fc->getAction, XML_SERIALIZER_OPTION_RETURN_RESULT => true ); $serializer = new XML_Serializer($options); RestUtils::sendResponse(200, $serializer->serialize($user_list), 'application/xml'); } break; // new user create case 'post': $user = new User; $user->setFirstName($data->getData->first_name); // just for example, this should be done cleaner // and so on... $user->save; // just send the new ID as the body RestUtils::sendResponse(201, $user->getId); break; }

再一次解释,这是一个例子,但它确实向我们展示了(至少我认为是)它能轻而易举的实现RESTful接口。

以是,这便是它。
我非常的自傲的说,我已经把这些阐明的非常清楚。
因此,我就不再赘述你如何详细实现它。

在一个真实的MVC运用中,大概你想要做的便是为你的每个接口创建一个单独的掌握器。
例如,利用上面的东西,我们可以创建一个UserRestController掌握器,这个掌握器有四个方法,分别为:get, put, post, 和 delete。
接口掌握器将会查看要求类型然后决定哪个方法会被实行。
这个方法会再利用工具来处理要求,处理数据,然后利用工具发送相应。

你大概会好比今更进一步,把你的接口掌握器和数据模型抽象出来,而不是明确的为每一个数据模型创建掌握器,你可以给你的接口掌握器添加一些逻辑,先去查找一个明确定义好的掌握器,如果没有,试着去查找一个已经存在的模型。
例如:网址'api/user/1'将会首先触发查找一个叫user的终极掌握器,如果没有,它会查找运用中叫user的模型,如果找到了,你可以写一个自动化的方法来自动处理所有要求这个模型的要求。

再进一步,你可以建立一个通用的'list-all'方法,就像上面一段中的例子一样。
假定你的url是'api/usrs',接口掌握器首先会查找叫users的掌握器,如果没有找到,确认users是复数,把它变成单数,然后查找一个叫user的模型,如果找到了,加载一个用户列表然后把他们发送出去。

末了,你可以给你的接口添加大略的身份验证。
假定你仅仅希望适当的验证访问你的接口的用户,那么,你可以在处理要求的方法中添加类似于下面的一些代码(借用我的一个现有运用,因此有一些常量和变量在这个代码片段里面并没有被定义):

Php代码

// figure out if we need to challenge the user if(emptyempty($_SERVER['PHP_AUTH_DIGEST'])) { header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Digest realm='' . AUTH_REALM . '',qop='auth',nonce='' . uniqid . '',opaque='' . md5(AUTH_REALM) . '''); // show the error if they hit cancel die(RestControllerLib::error(401, true)); } // now, analayze the PHP_AUTH_DIGEST var if(!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || $auth_username != $data['username']) { // show the error due to bad auth die(RestUtils::sendResponse(401)); } // so far, everything's good, let's now check the response a bit more... $A1 = md5($data['username'] . ':' . AUTH_REALM . ':' . $auth_pass); $A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']); $valid_response = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . $A2); // last check.. if($data['response'] != $valid_response) { die(RestUtils::sendResponse(401)); }

非常酷,对吧?通过少量的代码和一些智能的逻辑,你可以非常快速的给你的运用添加全功能的REST接口。
我并不仅仅是支持这个观点,我已经在我个人的框架里面实现了这些东西,而这些仅仅花费了半天的韶光,然后再花费半天韶光添加一些非常酷的东西。
如果你(读者)对我终极的实现感兴趣,请在评论中留言,我会非常乐趣和你分享它。
同时,如果你有什么比较酷的想法,也欢迎通过评论和我进行分享。
如果我足够喜好它,我会约请你在这里揭橥自己的文章。

标签:

相关文章

三字代码罗马古代罗马的智慧结晶

三字代码罗马,又称罗马数字,是古代罗马人创造的一种独特的数字符号系统。它以其简洁、直观的特点,在古代欧洲乃至全世界都产生了深远的影...

网站推广 2025-03-02 阅读0 评论0