# 快速入門(九):API開發
使用`ThinkPHP5.0`可以更簡單的進行`API`開發,并且最大程度的滿足`API`對性能的要求,下面就`API`開發中的幾個主要問題描述下。
- - [API版本](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/160681#api-)
- [異常處理](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/160681#u5F02u5E38u5904u7406)
- [RESTFul](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/160681#restful)
- [REST請求測試](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/160681#-code-rest-code-)
- [Postman](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/160681#postman)
- [REST請求偽裝](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/160681#-code-rest-code--1)
- [API調試](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/160681#api--1)
- [環境安裝](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/160681#u73AFu5883u5B89u88C5)
- [瀏覽器設置](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/160681#u6D4Fu89C8u5668u8BBEu7F6E)
- [應用配置](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/160681#u5E94u7528u914Du7F6E)
- [遠程調試](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/160681#u8FDCu7A0Bu8C03u8BD5)
- [安全建議](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/160681#u5B89u5168u5EFAu8BAE)
`5.0`對`API`開發的支持包括架構、功能和性能方面的良好支持。
## API版本
我們以一個用戶信息讀取的接口為例,包含兩個版本`V1`和`V2`,`v2`版本的接口包括用戶的檔案信息,統一使用`json`格式數據輸出到客戶端。
在`application`目錄下面創建`api`模塊目錄,并創建`controller`和`model`子目錄,因為`api`接口無需視圖,所以不需要創建`view`目錄。
`api`版本號的傳入方式有很多,包括設置頭信息、請求參數傳入以及路由方式,這里我們采請求參數傳入的方式,設置路由如下:
```
<pre class="calibre18">
```
<span class="hljs-operator"><span class="hljs-title1">Route</span>:<span class="hljs-string">:<span class="hljs-function">rule</span>(<span class="hljs-operator">':version/user/:id'</span>,<span class="hljs-operator">'api/:version.User/read'</span>)</span></span>;
```
```
不同版本的URL訪問地址為:
```
<pre class="calibre18">
```
<span class="hljs-string">http:</span>
<span class="hljs-comment">//tp5.com/v1/user/10</span>
<span class="hljs-string">http:</span>
<span class="hljs-comment">//tp5.com/v2/user/10</span>
```
```
> 版本號中不能包含`.`符號。
`v1`版本控制器(類文件位置為`application/api/controller/v1/User.php`)代碼如下:
```
<pre class="calibre18">
```
<span class="hljs-keyword">namespace</span> <span class="hljs-title">app</span>\<span class="hljs-title">api</span>\<span class="hljs-title">controller</span>\<span class="hljs-title">v1</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">app</span>\<span class="hljs-title">api</span>\<span class="hljs-title">model</span>\<span class="hljs-title">User</span> <span class="hljs-title">as</span> <span class="hljs-title">UserModel</span>;
<span class="hljs-operator"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span></span>{
<span class="hljs-comment">// 獲取用戶信息</span><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">read</span><span class="hljs-number">(<span class="hljs-regexp">$id</span> = <span class="hljs-operator">0</span>)</span></span>{
<span class="hljs-regexp">$user</span> = UserModel::get(<span class="hljs-regexp">$id</span>);
<span class="hljs-keyword">if</span> (<span class="hljs-regexp">$user</span>) {
<span class="hljs-keyword">return</span> json(<span class="hljs-regexp">$user</span>);
} <span class="hljs-keyword">else</span> {
<span class="hljs-keyword">return</span> json([<span class="hljs-string">'error'</span> => <span class="hljs-string">'用戶不存在'</span>], <span class="hljs-number">404</span>);
}
}
}
```
```
`v2`版本的控制器(類文件位置為`application/api/controller/v2/User.php`)代碼如下:
```
<pre class="calibre18">
```
<span class="hljs-keyword">namespace</span> <span class="hljs-title">app</span>\<span class="hljs-title">api</span>\<span class="hljs-title">controller</span>\<span class="hljs-title">v2</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">app</span>\<span class="hljs-title">api</span>\<span class="hljs-title">model</span>\<span class="hljs-title">User</span> <span class="hljs-title">as</span> <span class="hljs-title">UserModel</span>;
<span class="hljs-operator"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span></span>{
<span class="hljs-comment">// 獲取用戶信息</span><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">read</span><span class="hljs-number">(<span class="hljs-regexp">$id</span> = <span class="hljs-operator">0</span>)</span></span>{
<span class="hljs-regexp">$user</span> = UserModel::get(<span class="hljs-regexp">$id</span>, <span class="hljs-string">'profile'</span>);
<span class="hljs-keyword">if</span> (<span class="hljs-regexp">$user</span>) {
<span class="hljs-keyword">return</span> json(<span class="hljs-regexp">$user</span>);
} <span class="hljs-keyword">else</span> {
<span class="hljs-keyword">return</span> json([<span class="hljs-string">'error'</span> => <span class="hljs-string">'用戶不存在'</span>], <span class="hljs-number">404</span>);
}
}
}
```
```
v2版本和v1版本的接口區別在于v2的接口用戶信息包含了用戶的關聯檔案信息。
> 除了使用`json`格式返回客戶端之外,系統還支持`xml`、`jsonp`格式,只需要把上面的`json`函數更改為`xml`和`jsonp`即可。
`User`模型代碼如下:
```
<pre class="calibre18">
```
namespace app\api\model;
use think\<span class="hljs-operator">Model</span>;
<span class="hljs-operator"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> <span class="hljs-keyword"><span class="hljs-operator">extends</span></span> <span class="hljs-title">Model</span></span>{
<span class="hljs-comment">// 定義一對一關聯</span>
public function profile()
{
<span class="hljs-keyword">return</span> $<span class="hljs-keyword">this</span>->hasOne(<span class="hljs-operator">'Profil</span>e');
}
}
```
```
`Profile`模型代碼:
```
<pre class="calibre18">
```
namespace app\api\model;
use think\<span class="hljs-operator">Model</span>;
<span class="hljs-operator"><span class="hljs-keyword">class</span> <span class="hljs-title">Profile</span> <span class="hljs-keyword"><span class="hljs-operator">extends</span></span> <span class="hljs-title">Model</span></span>{
<span class="hljs-keyword">protected</span> $<span class="hljs-operator"><span class="hljs-keyword">type</span> =</span> [
<span class="hljs-operator">'birthda</span>y' => <span class="hljs-operator">'timestamp</span>:<span class="hljs-operator">Y</span>-m-d',
];
}
```
```
訪問 `http://tp5.com/v1/user/10` 返回的數據是:

訪問 `http://tp5.com/v2/user/10` 返回的數據是:

## 異常處理
當發生異常的時候,通常我們返回不同的`HTTP`狀態碼來標識,控制器的`read`方法中當請求的用戶不存在的時候,系統發送了`404`狀態碼來表示用戶數據不存在,代碼為:
```
<pre class="calibre18">
```
<span class="hljs-keyword">return</span> json([<span class="hljs-string">'error'</span> => <span class="hljs-string">'用戶不存在'</span>], <span class="hljs-number">404</span>);
```
```
并返回了錯誤信息如下:

客戶端通常可以直接通過HTTP狀態碼來判斷接口請求的成功與否而自行進行自定義錯誤提示,一般`400`以上的狀態碼都表示請求失敗,接口的代碼還可以簡化成:
```
<pre class="calibre18">
```
<span class="hljs-comment">// 獲取用戶信息</span><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">read</span><span class="hljs-number">(<span class="hljs-regexp">$id</span> = <span class="hljs-operator">0</span>)</span></span>{
<span class="hljs-regexp">$user</span> = UserModel::get(<span class="hljs-regexp">$id</span>, <span class="hljs-string">'profile'</span>);
<span class="hljs-keyword">if</span> (<span class="hljs-regexp">$user</span>) {
<span class="hljs-keyword">return</span> json(<span class="hljs-regexp">$user</span>);
} <span class="hljs-keyword">else</span> {
<span class="hljs-comment">// 拋出HTTP異常 并發送404狀態碼</span>
abort(<span class="hljs-number">404</span>);
}
}
```
```
如果你希望由API后臺來處理異常,并且直接接管系統的所有異常信息輸出json錯誤信息,可以自定義一個異常處理類位于(`application/api/exception/Http.php`):
```
<pre class="calibre18">
```
<span class="hljs-keyword">namespace</span> <span class="hljs-title">app</span>\<span class="hljs-title">api</span>\<span class="hljs-title">exception</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">think</span>\<span class="hljs-title">exception</span>\<span class="hljs-title">Handle</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">think</span>\<span class="hljs-title">exception</span>\<span class="hljs-title">HttpException</span>;
<span class="hljs-operator"><span class="hljs-keyword">class</span> <span class="hljs-title">Http</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Handle</span></span>{
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span><span class="hljs-number">(\Exception <span class="hljs-regexp">$e</span>)</span></span>{
<span class="hljs-keyword">if</span> (<span class="hljs-regexp">$e</span> <span class="hljs-keyword">instanceof</span> HttpException) {
<span class="hljs-regexp">$statusCode</span> = <span class="hljs-regexp">$e</span>->getStatusCode();
}
<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">isset</span>(<span class="hljs-regexp">$statusCode</span>)) {
<span class="hljs-regexp">$statusCode</span> = <span class="hljs-number">500</span>;
}
<span class="hljs-regexp">$result</span> = [
<span class="hljs-string">'code'</span> => <span class="hljs-regexp">$statusCode</span>,
<span class="hljs-string">'msg'</span> => <span class="hljs-regexp">$e</span>->getMessage(),
<span class="hljs-string">'time'</span> => <span class="hljs-regexp">$_SERVER</span>[<span class="hljs-string">'REQUEST_TIME'</span>],
];
<span class="hljs-keyword">return</span> json(<span class="hljs-regexp">$result</span>, <span class="hljs-regexp">$statusCode</span>);
}
}
```
```
然后,在應用配置文件中修改異常處理handle參數為自定義的異常類:
```
<pre class="calibre18">
```
<span class="hljs-string">'exception_handle'</span> => <span class="hljs-string">'\app\api\exception\Http'</span>,
```
```
接管`HTTP`異常處理后,我們可以直接在方法中拋出任何的`HTTP`異常,系統自動處理為`json`格式輸出到客戶端:
```
<pre class="calibre18">
```
<span class="hljs-comment">// 獲取用戶信息</span><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">read</span><span class="hljs-number">(<span class="hljs-regexp">$id</span> = <span class="hljs-operator">0</span>)</span></span>{
<span class="hljs-regexp">$user</span> = UserModel::get(<span class="hljs-regexp">$id</span>, <span class="hljs-string">'profile'</span>);
<span class="hljs-keyword">if</span> (<span class="hljs-regexp">$user</span>) {
<span class="hljs-keyword">return</span> json(<span class="hljs-regexp">$user</span>);
} <span class="hljs-keyword">else</span> {
<span class="hljs-comment">// 拋出HTTP異常 并發送404狀態碼</span>
abort(<span class="hljs-number">404</span>,<span class="hljs-string">'用戶不存在'</span>);
}
}
```
```
當我們請求一個不存在的用戶時候
```
<pre class="calibre18">
```
<span class="hljs-string">http:</span>
<span class="hljs-comment">//tp5.com/v2/user/100</span>
```
```
會看到如下的輸出:

如果希望捕獲系統的任何異常并轉發,可以使用`try catch`如下:
```
<pre class="calibre18">
```
<span class="hljs-comment">// 獲取用戶信息</span><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">read</span><span class="hljs-number">(<span class="hljs-regexp">$id</span> = <span class="hljs-operator">0</span>)</span></span>{
<span class="hljs-keyword">try</span> {
<span class="hljs-comment">// 制造一個方法不存在的異常</span><span class="hljs-regexp">$user</span> = UserModel::geet(<span class="hljs-regexp">$id</span>, <span class="hljs-string">'profile'</span>);
<span class="hljs-keyword">if</span> (<span class="hljs-regexp">$user</span>) {
<span class="hljs-keyword">return</span> json(<span class="hljs-regexp">$user</span>);
} <span class="hljs-keyword">else</span> {
<span class="hljs-keyword">return</span> abort(<span class="hljs-number">404</span>, <span class="hljs-string">'用戶不存在'</span>);
}
} <span class="hljs-keyword">catch</span> (\<span class="hljs-keyword">Exception</span> <span class="hljs-regexp">$e</span>) {
<span class="hljs-comment">// 捕獲異常并轉發為HTTP異常</span><span class="hljs-keyword">return</span> abort(<span class="hljs-number">404</span>, <span class="hljs-regexp">$e</span>->getMessage());
}
}
```
```
執行后會看到系統輸出了

## RESTFul
`REST`(Representational State Transfer表述性狀態轉移)是一種針對網絡應用的設計和開發方式,可以降低開發的復雜性,提高系統的可伸縮性。`REST`提出了一些設計概念和準則:
1、網絡上的所有事物都被抽象為資源(resource);
2、每個資源對應一個唯一的資源標識(resource identifier);
3、通過通用的連接器接口(generic connector interface)對資源進行操作;
4、對資源的各種操作不會改變資源標識;
5、所有的操作都是無狀態的(stateless)。
`REST`通常基于使用`HTTP`,`URI`,和`JSON`以及`HTML`這些現有的廣泛流行的協議和標準。
傳統的請求模式和`REST`模式的請求模式區別:
作用 傳統模式 REST模式 列舉出所有的用戶 GET /users/list GET /users 列出ID為1的用戶信息 GET /users/show/id/1 GET /users/1 插入一個新的用戶 POST /users/add POST /users 更新ID為1的用戶信息 POST /users/update/id/1 PUT /users/1 刪除ID為1的用戶 POST /users/delete/id/1 DELETE /users/1關于更多的REST信息,可以參考:<http://zh.wikipedia.org/wiki/REST>
下面以一個博客的`REST`接口開發為例,先創建`think_blog`數據表如下:
```
<pre class="calibre18">
```
<span class="hljs-operator"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">IF</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span> <span class="hljs-string">`think_blog`</span> (
<span class="hljs-string">`id`</span> <span class="hljs-number">int</span>(<span class="hljs-number">10</span>) <span class="hljs-keyword">UNSIGNED</span> <span class="hljs-keyword">NOT</span> <span class="hljs-number">NULL</span> AUTO_INCREMENT <span class="hljs-keyword">COMMENT</span> <span class="hljs-string">'ID'</span>,
<span class="hljs-string">`name`</span> <span class="hljs-number">char</span>(<span class="hljs-number">40</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-number">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-string">''</span> <span class="hljs-keyword">COMMENT</span> <span class="hljs-string">'標識'</span>,
<span class="hljs-string">`title`</span> <span class="hljs-number">char</span>(<span class="hljs-number">80</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-number">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-string">''</span> <span class="hljs-keyword">COMMENT</span> <span class="hljs-string">'標題'</span>,
<span class="hljs-string">`content`</span> <span class="hljs-number">text</span> <span class="hljs-keyword">COMMENT</span> <span class="hljs-string">'內容'</span>,
<span class="hljs-string">`create_time`</span> <span class="hljs-number">int</span>(<span class="hljs-number">10</span>) <span class="hljs-keyword">UNSIGNED</span> <span class="hljs-keyword">NOT</span> <span class="hljs-number">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-string">'0'</span> <span class="hljs-keyword">COMMENT</span> <span class="hljs-string">'創建時間'</span>,
<span class="hljs-string">`update_time`</span> <span class="hljs-number">int</span>(<span class="hljs-number">10</span>) <span class="hljs-keyword">UNSIGNED</span> <span class="hljs-keyword">NOT</span> <span class="hljs-number">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-string">'0'</span> <span class="hljs-keyword">COMMENT</span> <span class="hljs-string">'更新時間'</span>,
<span class="hljs-string">`status`</span> tinyint(<span class="hljs-number">1</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-number">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-string">'0'</span> <span class="hljs-keyword">COMMENT</span> <span class="hljs-string">'數據狀態'</span>,
PRIMARY <span class="hljs-keyword">KEY</span> (<span class="hljs-string">`id`</span>)
) <span class="hljs-keyword">ENGINE</span>=MyISAM <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CHARSET</span>=utf8 <span class="hljs-keyword">COMMENT</span>=<span class="hljs-string">'博客表'</span>;</span>
```
```
> 為了演示需要,該數據表做了一定程度的簡化,并不一定符合實際的博客設計,因此僅供測試學習。
為了支持`RESTFul`請求的路由規則,只需要在路由配置文件中添加下面的代碼:
```
<pre class="calibre18">
```
<span class="hljs-operator"><span class="hljs-title1">Route</span>:<span class="hljs-string">:<span class="hljs-function">resource</span>(<span class="hljs-operator">'blogs'</span>,<span class="hljs-operator">'index/blog'</span>)</span></span>;
```
```
該方法注冊了一個名為`blogs`的資源路由,其實內部會自動注冊`7`個路由規則,包括:
標識 請求類型 生成路由規則 對應操作方法(默認) 描述 index GET `blogs` index 顯示博客列表 create GET `blogs/create` create 新增博客頁面 save POST `blogs` save 保存博客內容 read GET `blogs/:id` read 查看博客內容 edit GET `blogs/:id/edit` edit 編輯博客頁面 update PUT `blogs/:id` update 更新博客內容 delete DELETE `blogs/:id` delete 刪除博客> 上面的7個路由規則,在本示例中僅使用了5個(其中create和edit頁面由于本API接口測試暫時不需要使用)。
創建Blog模型如下:
```
<pre class="calibre18">
```
<span class="hljs-operator"><span class="hljs-number"><?php</span><span class="hljs-keyword">namespace</span> <span class="hljs-title">app</span>\<span class="hljs-title">index</span>\<span class="hljs-title">model</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">think</span>\<span class="hljs-title">Model</span>;
<span class="hljs-operator"><span class="hljs-keyword">class</span> <span class="hljs-title">Blog</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Model</span></span>{
<span class="hljs-keyword">protected</span> <span class="hljs-regexp">$autoWriteTimestamp</span> = <span class="hljs-keyword">true</span>;
<span class="hljs-keyword">protected</span> <span class="hljs-regexp">$insert</span> = [
<span class="hljs-string">'status'</span> => <span class="hljs-number">1</span>,
];
<span class="hljs-keyword">protected</span> <span class="hljs-regexp">$field</span> = [
<span class="hljs-string">'id'</span> => <span class="hljs-string">'int'</span>,
<span class="hljs-string">'create_time'</span> => <span class="hljs-string">'int'</span>,
<span class="hljs-string">'update_time'</span> => <span class="hljs-string">'int'</span>,
<span class="hljs-string">'name'</span>, <span class="hljs-string">'title'</span>, <span class="hljs-string">'content'</span>,
];
}</span>
```
```
為了快速生成控制器類,我們進入命令行,切換到應用根目錄下面,執行下面的指令:
```
<pre class="calibre18">
```
php think make:controller index/B<span class="hljs-number">log</span>
```
```
會自動生成一個`Blog`資源控制器類,并且會自動生成如前所述的資源路由對應的7個(空白)方法,我們給每個方法添加一些簡單的代碼,并去掉了`create`和`edit`兩個方法(對于`api`開發而言可以不需要),最終控制器代碼如下:
```
<pre class="calibre18">
```
<span class="hljs-operator"><span class="hljs-number"><?php</span><span class="hljs-keyword">namespace</span> <span class="hljs-title">app</span>\<span class="hljs-title">index</span>\<span class="hljs-title">controller</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">app</span>\<span class="hljs-title">index</span>\<span class="hljs-title">model</span>\<span class="hljs-title">Blog</span> <span class="hljs-title">as</span> <span class="hljs-title">Blogs</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">think</span>\<span class="hljs-title">Controller</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">think</span>\<span class="hljs-title">Request</span>;
<span class="hljs-operator"><span class="hljs-keyword">class</span> <span class="hljs-title">Blog</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span></span>{
<span class="hljs-comment">/**
* 顯示資源列表
*
* <span class="hljs-operator">@return</span> \think\Response
*/</span><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span><span class="hljs-number">()</span></span>{
<span class="hljs-regexp">$list</span> = Blogs::all();
<span class="hljs-keyword">return</span> json(<span class="hljs-regexp">$list</span>);
}
<span class="hljs-comment">/**
* 保存新建的資源
*
* <span class="hljs-operator">@param</span> \think\Request $request
* <span class="hljs-operator">@return</span> \think\Response
*/</span><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">save</span><span class="hljs-number">(Request <span class="hljs-regexp">$request</span>)</span></span>{
<span class="hljs-regexp">$data</span> = <span class="hljs-regexp">$request</span>->param();
<span class="hljs-regexp">$result</span> = Blogs::create(<span class="hljs-regexp">$data</span>);
<span class="hljs-keyword">return</span> json(<span class="hljs-regexp">$result</span>);
}
<span class="hljs-comment">/**
* 顯示指定的資源
*
* <span class="hljs-operator">@param</span> int $id
* <span class="hljs-operator">@return</span> \think\Response
*/</span><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">read</span><span class="hljs-number">(<span class="hljs-regexp">$id</span>)</span></span>{
<span class="hljs-regexp">$data</span> = Blogs::get(<span class="hljs-regexp">$id</span>);
<span class="hljs-keyword">return</span> json(<span class="hljs-regexp">$data</span>);
}
<span class="hljs-comment">/**
* 保存更新的資源
*
* <span class="hljs-operator">@param</span> \think\Request $request
* <span class="hljs-operator">@param</span> int $id
* <span class="hljs-operator">@return</span> \think\Response
*/</span><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">update</span><span class="hljs-number">(Request <span class="hljs-regexp">$request</span>, <span class="hljs-regexp">$id</span>)</span></span>{
<span class="hljs-regexp">$data</span> = <span class="hljs-regexp">$request</span>->param();
<span class="hljs-regexp">$result</span> = Blogs::update(<span class="hljs-regexp">$data</span>, [<span class="hljs-string">'id'</span> => <span class="hljs-regexp">$id</span>]);
<span class="hljs-keyword">return</span> json(<span class="hljs-regexp">$result</span>);
}
<span class="hljs-comment">/**
* 刪除指定資源
*
* <span class="hljs-operator">@param</span> int $id
* <span class="hljs-operator">@return</span> \think\Response
*/</span><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">delete</span><span class="hljs-number">(<span class="hljs-regexp">$id</span>)</span></span>{
<span class="hljs-regexp">$result</span> = Blogs::destroy(<span class="hljs-regexp">$id</span>);
<span class="hljs-keyword">return</span> json(<span class="hljs-regexp">$result</span>);
}
}</span>
```
```
> 作為學習用途,Blog接口沒有添加數據驗證,大家可以結合之前學習的模型驗證功能自己添加。
由于`API`開發沒有實際的頁面顯示,加上`REST`請求類型復雜,我們需要通過特殊的方式才能在開發過程中進行`REST`請求的測試。
## `REST`請求測試
`REST`請求的測試方式有很多,下面介紹兩種常用的方式:
### Postman
最方便的方法就是通過Postman來測試接口,給Chrome瀏覽器安裝一個`postman`擴展,訪問下面地址獲取官方擴展:<https://www.getpostman.com/>

或者直接中chrome的應用商店搜索`postman`(由于眾所周知的原因,可能無法正常訪問)。

安裝完成后會打開

點擊`Postman`應用圖標就可以打開應用。
如果是首次使用的話,會首先要求注冊用戶,完成后會進入主界面:

下面我們就來測試下前面的`REST`應用的接口,測試之前選擇對應的請求類型,并輸入我們的接口地址,如果需要傳入參數,則點擊`Send`按鈕之前的`Params`,依次輸入`key`(參數名稱)和`value`(參數值),然后點擊`Send`按鈕即可,下面依次測試博客的接口。
> #### 博客添加接口:post <http://tp5.com/blogs>

通過添加接口我們寫入了兩條數據。
> #### 博客讀取接口:get <http://tp5.com/blogs/1>

> #### 博客更新接口:put <http://tp5.com/blogs/1>

> #### 博客列表接口:get <http://tp5.com/blogs>

> #### 刪除博客接口:delete <http://tp5.com/blogs/1>

如果某個接口出現錯誤,可以點擊`Preview`查看錯誤頁面。
### `REST`請求偽裝
除了使用`Postman`之外,可以通過一個`post`表單來偽裝`REST`的請求類型。首先創建一個普通的`post`表單,把請求參數都作為表單的項目,并且在表單最后添加一個隱藏域`_method`,下面的表單模擬了刪除博客的請求接口。
```
<pre class="calibre18">
```
<form method=<span class="hljs-string">"post"</span> <span class="hljs-operator"><span class="hljs-keyword">class</span>=</span><span class="hljs-string">"form"</span> action=<span class="hljs-string">"/blogs/1"</span>>
<input <span class="hljs-operator"><span class="hljs-keyword">type</span>=</span><span class="hljs-string">"submit"</span> <span class="hljs-operator"><span class="hljs-keyword">class</span>=</span><span class="hljs-string">"btn"</span> value=<span class="hljs-string">" 刪除 "</span>>
<input <span class="hljs-operator"><span class="hljs-keyword">type</span>=</span><span class="hljs-string">"hidden"</span> name=<span class="hljs-string">"_method"</span> value=<span class="hljs-string">"DELETE"</span> />
</form>
```
```
## API調試
可以使用`ThinkPHP5.0`的`Trace`調試中的`Socket`調試功能來解決`API`開發的調試問題。
### 環境安裝
如果你首次使用,參考下面的安裝辦法進行`SocketLog`的安裝。
> #### SocketLog安裝方法
>
> 首先,請在chrome瀏覽器上安裝好插件。
> SocketLog首先需要安裝chrome插件,Chrome插件[安裝頁面](https://chrome.google.com/webstore/detail/socketlog/apkmbfpihjhongonfcgdagliaglghcod) (需翻墻)
>
> 安裝服務端(如果沒有nodejs和npm 請首先安裝,[安裝參考](http://www.infoq.com/cn/articles/nodejs-npm-install-config)),運行下面指令:
>
> ```
> <pre class="calibre25">
> ```
> npm <span class="hljs-operator"><span class="hljs-keyword">install</span> -<span class="hljs-keyword">g</span> socketlog-<span class="hljs-keyword">server</span></span>
> ```
>
> ```
>
> 安裝完成后, 運行命令
>
> ```
> <pre class="calibre25">
> ```
> socketlog-<span class="hljs-number">server</span>
> ```
>
> ```
>
> 即可啟動服務。 將會在本地起一個websocket服務 ,監聽端口是1229 。
>
> 如果想服務后臺運行,使用:
>
> ```
> <pre class="calibre21">
> ```
> socketlog-<span class="hljs-number">server</span> > /dev/<span class="hljs-number">null</span> &
> ```
>
> ```
### 瀏覽器設置
首次使用的時候,需要點擊Chrome擴展進行如下設置:

Client\_ID用于標識當前用戶,注意不要沖突。
### 應用配置
接下來,修改應用配置文件,修改如下參數:
```
<pre class="calibre18">
```
<span class="hljs-string">'log'</span> => [
<span class="hljs-string">'type'</span> => <span class="hljs-string">'socket'</span>,
<span class="hljs-string">'host'</span> => <span class="hljs-string">'localhost'</span>,
<span class="hljs-string">'show_included_files'</span> => <span class="hljs-keyword">true</span>,
<span class="hljs-string">'force_client_ids'</span> => [<span class="hljs-string">'slog_b6d7ef'</span>],
<span class="hljs-string">'allow_client_ids'</span> => [<span class="hljs-string">'slog_b6d7ef'</span>],
],
```
```
該配置參數把日志類型設置為socket,所有的日志都會寫到socket服務器。
如果你的`socket-server`服務器不是`localhost`,配置使用ip地址或者域名即可。
### 遠程調試
下面就可以進行遠程調試了,你可以使用任何瀏覽器訪問:
```
<pre class="calibre18">
```
<span class="hljs-string">http:</span>
<span class="hljs-comment">//tp5.com/v2/user/10</span>
```
```
然后在`Chrome`瀏覽器中打開`Console`,就可以隨時查看該`API`應用的遠程調試信息了。

一旦有請求產生,就會自動刷新`Console`信息顯示。
還可以支持異常和錯誤信息的記錄,例如:

如果需要指定多個`client_id`進行調試,只需要配置`force_client_ids`和`allow_client_ids`為多個參數即可,例如:
```
<pre class="calibre18">
```
<span class="hljs-string">'log'</span> => [
<span class="hljs-string">'type'</span> => <span class="hljs-string">'socket'</span>,
<span class="hljs-string">'host'</span> => <span class="hljs-string">'localhost'</span>,
<span class="hljs-string">'show_included_files'</span> => <span class="hljs-keyword">true</span>,
<span class="hljs-string">'force_client_ids'</span> => [<span class="hljs-string">'slog_b6d7ef'</span>,<span class="hljs-string">'slog_abd89d'</span>],
<span class="hljs-string">'allow_client_ids'</span> => [<span class="hljs-string">'slog_b6d7ef'</span>,<span class="hljs-string">'slog_abd89d'</span>],
],
```
```
## 安全建議
- 盡量采用HTTPS協議進行接口請求;
- 重要的功能加密傳輸;
- 做好接口的身份認證;
- 對URL中的參數做好安全過濾;
- 對接口請求做好請求速率限制;
- 重要ID不透明處理;
- 使用JSON格式返回數據;
詳細可以參考 [《REST API 安全設計指南》](http://www.hmoore.net/kancloud/rest-api-design-safety)
- 脕茫隆壟脨貌脩脭
- 脕茫隆壟脨貌脩脭
- 脪祿隆壟祿霉麓隆
- 脪祿隆壟祿霉麓隆
- 露鎂隆壟URL潞脥脗路脫脡
- 露鎂隆壟URL潞脥脗路脫脡
- 脠媒隆壟脟毛脟貿潞脥脧矛脫婁
- 脠媒隆壟脟毛脟貿潞脥脧矛脫婁
- 脣脛隆壟脢媒戮脻驢芒
- 脣脛隆壟脢媒戮脻驢芒
- 脦氓隆壟虜茅脩爐脫茂脩脭
- 脦氓隆壟虜茅脩爐脫茂脩脭
- 脕霉隆壟脛攏脨脥潞脥鹿脴脕陋
- 攏簍1攏漏脛攏脨脥露簍脪氓
- 攏簍2攏漏祿霉麓隆虜脵脳梅
- 攏簍3攏漏露脕脠隆脝梅潞脥脨脼賂脛脝梅
- 攏簍4攏漏脌脿脨脥脳陋祿祿潞脥脳脭露爐脥錨魯脡
- 攏簍5攏漏虜茅脩爐路露脦摟
- 攏簍6攏漏脢盲脠毛潞脥脩茅脰隴
- 攏簍7攏漏鹿脴脕陋
- 攏簍8攏漏脛攏脨脥脢盲魯枚
- 脝脽隆壟脢脫脥錄潞脥脛攏擄氓
- 脝脽隆壟脢脫脥錄潞脥脛攏擄氓
- 擄脣隆壟碌梅脢脭潞脥脠脮脰戮
- 擄脣隆壟碌梅脢脭潞脥脠脮脰戮
- 戮脜隆壟API驢陋路壟
- 戮脜隆壟API驢陋路壟
- 脢廬隆壟脙眉脕卯脨脨鹿隴戮脽
- 脢廬隆壟脙眉脕卯脨脨鹿隴戮脽
- 脢廬脪祿隆壟脌漏脮鹿
- 脢廬脪祿隆壟脌漏脮鹿
- 脢廬露鎂隆壟脭脫脧卯
- Cookie
- Session
- 碌樓脭陋虜芒脢脭
- 脥錄脧帽麓婁脌鉚
- 脦脛錄鎂脡脧麓蘆
- 脩茅脰隴脗毛
- 賂陸脗錄
- A隆壟魯攏錄沒脦脢脤芒錄爐
- B隆壟3.2潞脥5.0脟酶鹵冒
- C隆壟脰煤脢脰潞爐脢媒
- 路盧脥芒脝陋攏潞脩摟脧擄ThinkPHP5碌脛脮媒脠路脳脣脢脝
- 路盧脥芒脝陋攏潞脩摟脧擄ThinkPHP5碌脛脮媒脠路脳脣脢脝