如何构建一个RESTful API?

77
问题如下: 我有一个在PHP服务器上运行的Web应用程序,我想为其构建REST API。
我做了一些研究,发现REST API使用HTTP方法(GET、POST...)用于某些URI,其中包含身份验证密钥(不一定需要),信息以XML或JSON格式作为HTTP响应返回(我更喜欢JSON)。

我的问题是:

  1. 作为应用程序开发人员,我如何构建这些URI?我需要在该URI上编写PHP代码吗?
  2. 如何构建JSON对象以作为响应返回?

1
http://www.gen-x-design.com/archives/create-a-rest-api-with-php/ 是一个非常好的关于此方面的资源。 - Geoffrey Wagner
我写了这篇博客文章,其中包含一些示例代码和说明。 - mevdschee
7个回答

69

以下是一个简单的 PHP 示例。

有两个文件 client.phpapi.php。我将这两个文件都放在相同的 URL 上:http://localhost:8888/,因此您需要将链接更改为自己的 URL。(这两个文件可以位于两个不同的服务器上)。

这只是一个示例,它很简单,也很简陋,而且我已经很久没写过 PHP 了。但这就是 API 的基本思想。

client.php

<?php

/*** this is the client ***/


if (isset($_GET["action"]) && isset($_GET["id"]) && $_GET["action"] == "get_user") // if the get parameter action is get_user and if the id is set, call the api to get the user information
{
  $user_info = file_get_contents('http://localhost:8888/api.php?action=get_user&id=' . $_GET["id"]);
  $user_info = json_decode($user_info, true);

  // THAT IS VERY QUICK AND DIRTY !!!!!
  ?>
    <table>
      <tr>
        <td>Name: </td><td> <?php echo $user_info["last_name"] ?></td>
      </tr>
      <tr>
        <td>First Name: </td><td> <?php echo $user_info["first_name"] ?></td>
      </tr>
      <tr>
        <td>Age: </td><td> <?php echo $user_info["age"] ?></td>
      </tr>
    </table>
    <a href="http://localhost:8888/client.php?action=get_userlist" alt="user list">Return to the user list</a>
  <?php
}
else // else take the user list
{
  $user_list = file_get_contents('http://localhost:8888/api.php?action=get_user_list');
  $user_list = json_decode($user_list, true);
  // THAT IS VERY QUICK AND DIRTY !!!!!
  ?>
    <ul>
    <?php foreach ($user_list as $user): ?>
      <li>
        <a href=<?php echo "http://localhost:8888/client.php?action=get_user&id=" . $user["id"]  ?> alt=<?php echo "user_" . $user_["id"] ?>><?php echo $user["name"] ?></a>
    </li>
    <?php endforeach; ?>
    </ul>
  <?php
}

?>

api.php

<?php

// This is the API to possibility show the user list, and show a specific user by action.

function get_user_by_id($id)
{
  $user_info = array();

  // make a call in db.
  switch ($id){
    case 1:
      $user_info = array("first_name" => "Marc", "last_name" => "Simon", "age" => 21); // let's say first_name, last_name, age
      break;
    case 2:
      $user_info = array("first_name" => "Frederic", "last_name" => "Zannetie", "age" => 24);
      break;
    case 3:
      $user_info = array("first_name" => "Laure", "last_name" => "Carbonnel", "age" => 45);
      break;
  }

  return $user_info;
}

function get_user_list()
{
  $user_list = array(array("id" => 1, "name" => "Simon"), array("id" => 2, "name" => "Zannetie"), array("id" => 3, "name" => "Carbonnel")); // call in db, here I make a list of 3 users.

  return $user_list;
}

$possible_url = array("get_user_list", "get_user");

$value = "An error has occurred";

if (isset($_GET["action"]) && in_array($_GET["action"], $possible_url))
{
  switch ($_GET["action"])
    {
      case "get_user_list":
        $value = get_user_list();
        break;
      case "get_user":
        if (isset($_GET["id"]))
          $value = get_user_by_id($_GET["id"]);
        else
          $value = "Missing argument";
        break;
    }
}

exit(json_encode($value));

?>

这个例子中我没有对数据库进行任何调用,但通常情况下,您应该这样做。您还应该将"file_get_contents"函数替换为"curl"。


你可以随意命名文件。但是,最好使用明确的名称来调用文件。我在我的API文件中检查了获取参数,并根据该值调用了一个函数。你可以包含 user.php 并调用此文件中的函数,或者将文件放在 api/user 中并从这里处理参数。 - Simon marc
1
是的,但这与创建普通网站相同,只是你发送回来的信息不同。 - Simon marc
我可以在电信行业使用REST发送短信吗? - XxXk5XxX
你可能想要将内容类型标头设置为 application/json(https://dev59.com/c2855IYBdhLWcg3w75Iv),并且在使用 Angular(等)时,回调参数可能会很方便(https://dev59.com/p2ct5IYBdhLWcg3wetQo)。 - jsruok
6
这里仅仅使用了“GET”请求,没有考虑请求方法,因此并不符合RESTful标准。请参考https://dev59.com/n3RC5IYBdhLWcg3wROpQ#897311中的示例,以及http://en.wikipedia.org/wiki/Representational_state_transfer#Applied_to_web_services。 - Luke
显示剩余2条评论

34

在2013年,你应该使用类似SilexSlim的东西。

Silex 示例:

require_once __DIR__.'/../vendor/autoload.php'; 

$app = new Silex\Application(); 

$app->get('/hello/{name}', function($name) use($app) { 
    return 'Hello '.$app->escape($name); 
}); 

$app->run(); 

简洁示例:

$app = new \Slim\Slim();
$app->get('/hello/:name', function ($name) {
    echo "Hello, $name";
});
$app->run();

1
谢谢。我使用了Slim框架,只用几分钟就能运行一个简单的API。 - user12345
你会推荐什么更复杂(详细)的API?Slim很方便,但所有功能都在单个文件中,因此可读性非常差。 - Ilker Baltaci
2
使用Silex,您可以将控制器提取到类中。这里有一篇关于此的好文章,以及使用微框架与全栈框架的优缺点https://igor.io/2012/11/09/scaling-silex.html。我个人更喜欢Silex,因为它为我提供了最重要的东西,并让我选择其余的堆栈。 - holographic-principle

11

这基本上就像创建一个普通网站一样。

PHP网站的正常模式是:

  1. 用户输入URL
  2. 服务器获取URL,解析它并执行一个操作
  3. 在此操作中,您获取/生成页面所需的所有信息
  4. 使用来自操作的信息创建HTML/PHP页面
  5. 服务器生成完整的HTML页面并将其发送回用户

使用API,只需在第3步和第4步之间添加一个新步骤。在第3步之后,创建一个包含您需要的所有信息的数组。将该数组编码为JSON,并退出或返回该值。

$info = array("info_1" => 1; "info_2" => "info_2" ... "info_n" => array(1,2,3));
exit(json_encode($info));

这就是关于 API 的全部内容。 对于客户端,您可以通过 URL 调用 API。如果该 API 仅适用于 GET 请求,则我认为可以使用简单的方式进行操作(为了检查,我通常使用 curl)。

$info = file_get_contents(url);
$info = json_decode($info);

通常使用curl库执行get和post调用更为常见。如果需要使用curl,请向我提问。

一旦从API获取信息,您可以执行第4和第5步操作。

查看php文档中的json函数和file_get_contents。

curl: http://fr.php.net/manual/fr/ref.curl.php


编辑

不,等等,我不明白。 "php API页面"你指的是什么?

API仅用于创建/检索项目。如果您正在制作网站,则永远不会直接通过API发送HTML结果。您使用URL调用API,API返回信息,然后使用此信息创建最终结果。

例如:您想编写一个HTML页面以向xxx打招呼。但是,要获取用户的名称,您必须从API获取信息。

因此,假设您的API具有一个函数,该函数以user_id为参数并返回此用户的名称(假设为getUserNameById(user_id)),则只需在类似于your/api/ulr/getUser/id的URL上调用此函数即可。

Function getUserNameById(user_id)
{
  $userName = // call in db to get the user
  exit(json_encode($userName)); // maybe return work as well.
}

客户端角度来看,你需要做的是

    $username = file_get_contents(your/api/url/getUser/15); // You should normally use curl, but it simpler for the example
// So this function to this specifique url will call the api, and trigger the getUserNameById(user_id), whom give you the user name.
    <html>
    <body>
    <p>hello <?php echo $username ?> </p>
    </body>
    </html>

所以客户端从未直接访问数据库,这是API的作用。

这样清晰明了吗?


另外需要注意的是,API应该为响应设置正确的内容类型。对于JSON,请参见https://dev59.com/cXRB5IYBdhLWcg3w4bKv - Ben
那我应该在服务器上有一个PHP API页面来管理所有请求吗? - Sharon Haim Pour
你想要哪些文件,它与创建网站完全相同,只是您从客户端调用API,而不是从浏览器调用。但对于API而言,没有什么改变。当您在服务器上收到URL时,可以通过GET和POST参数决定如何处理它。您已经制作过网站了吗?您使用过像Cake、Symfony或Zend这样的框架吗? - Simon marc
谢谢你的帮助。我的网站已经上线了,我没有使用任何框架来构建它。服务器端是用PHP编写的。如果您能够这样做并编写某种API调用,我将不胜感激。 - Sharon Haim Pour
我只是給你一個小例子。我試了一下,它運作正常。如果你有任何問題,告訴我。 - Simon marc
显示剩余5条评论

8

(1) 我该如何构建那些URI?我需要在那个URI上编写PHP代码吗?

API URI方案的设置没有标准,但通常采用斜杠分隔的值。为此,您可以使用...

$apiArgArray = explode("/", substr(@$_SERVER['PATH_INFO'], 1));

要在文件名后面获取URI中斜杠分隔值的数组,请使用以下方法:

例如:假设您的应用程序中有一个API文件api.php,并且您请求api.php/members/3,那么$apiArgArray将是一个包含['members','3']的数组。然后,您可以使用这些值来查询数据库或进行其他处理。

(2) 如何构建JSON对象以作为响应返回?

您可以使用json_encode将任何PHP对象转换为JSON。您还需要设置适当的标头。

header('Content-Type: application/json');
$myObject = (object) array( 'property' => 'value' ); // example
echo json_encode($myObject); // outputs JSON text

所有这些对于返回JSON的API来说都是很好的,但你接下来应该问的问题是:

(3) 如何使我的API符合RESTful标准?

为此,我们将使用$_SERVER['REQUEST_METHOD']来获取正在使用的方法,然后根据不同的方法执行不同的操作。因此,最终的结果类似于...

header('Content-Type: application/json');
$apiArgArray = explode("/", substr(@$_SERVER['PATH_INFO'], 1));
$returnObject = (object) array();
/* Based on the method, use the arguments to figure out
   whether you're working with an individual or a collection, 
   then do your processing, and ultimately set $returnObject */
switch ($_SERVER['REQUEST_METHOD']) {
  case 'GET':
    // List entire collection or retrieve individual member
    break;
  case 'PUT':       
    // Replace entire collection or member
    break;  
  case 'POST':      
    // Create new member
    break;
  case 'DELETE':    
    // Delete collection or member
    break;
}
echo json_encode($returnObject);

来源:https://dev59.com/n3RC5IYBdhLWcg3wROpQ#897311http://en.wikipedia.org/wiki/Representational_state_transfer#Applied_to_web_services
(说明:此文需要翻译的部分为英文链接)

2

2

我知道这个问题已经被接受并且有一些历史,但这可能对一些仍然认为它相关的人有所帮助。虽然结果不是完全符合RESTful API标准,但是PHP的API Builder mini lib可以让你轻松地将MySQL数据库转换为可通过Web访问的JSON API。


0

正如Simon Marc所说,这个过程与我们浏览网站的过程基本相同。如果您熟悉使用Zend框架,有一些易于跟随的教程可以让您轻松设置。构建RESTful API最困难的部分是设计它,并使其真正符合RESTful标准,在数据库术语中考虑CRUD。

也许您真的想要一个XML-RPC接口或类似的东西。您希望此接口允许您做什么?

--编辑

以下是我开始使用RESTful API和Zend Framework的地方。 Zend Framework Example

简而言之,不要使用Zend rest server,因为它已经过时了。


如果您感兴趣,我将在我的博客上发布一些关于如何使用ZF和RESTful Web服务的入门内容,并提供一个完整的工作示例。 - James Butler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接