# 快速入门(九):API开发
使用`ThinkPHP5.0`可以更简单的进行`API`开发,并且最大程度的满足`API`对性能的要求,下面就`API`开发中的几个主要问题描述下。
- - [API版本](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/160681#api-)
- [异常处理](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/160681#u5F02u5E38u5904u7406)
- [RESTFul](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/160681#restful)
- [REST请求测试](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/160681#-code-rest-code-)
- [Postman](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/160681#postman)
- [REST请求伪装](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/160681#-code-rest-code--1)
- [API调试](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/160681#api--1)
- [环境安装](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/160681#u73AFu5883u5B89u88C5)
- [浏览器设置](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/160681#u6D4Fu89C8u5668u8BBEu7F6E)
- [应用配置](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/160681#u5E94u7528u914Du7F6E)
- [远程调试](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/160681#u8FDCu7A0Bu8C03u8BD5)
- [安全建议](http://ihavenolimitations.xyz/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` 返回的数据是:
![](https://img.kancloud.cn/7b/d9/7bd9206ace494d194cfe7e5310e41925_1109x208.png)
访问 `http://tp5.com/v2/user/10` 返回的数据是:
![](https://img.kancloud.cn/c7/6e/c76e8fa2bf855bbf556733191d3bff62_1261x334.png)
## 异常处理
当发生异常的时候,通常我们返回不同的`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>);
```
```
并返回了错误信息如下:
![](https://img.kancloud.cn/bc/20/bc2098bd98334fe48d5a3c7b85f03e7c_326x79.png)
客户端通常可以直接通过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>
```
```
会看到如下的输出:
![](https://img.kancloud.cn/48/db/48db83f6c69d63d6948aee8ac63b9528_534x106.png)
如果希望捕获系统的任何异常并转发,可以使用`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());
}
}
```
```
执行后会看到系统输出了
![](https://img.kancloud.cn/44/a1/44a10223ba9ac0d2216757f47838670e_781x115.png)
## 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/>
![](https://img.kancloud.cn/c2/02/c2027e464c794e6bf39a94223470cf4f_718x489.png)
或者直接中chrome的应用商店搜索`postman`(由于众所周知的原因,可能无法正常访问)。
![](https://img.kancloud.cn/86/96/86962f47543eb18cdcc3346712837454_1193x692.png)
安装完成后会打开
![](https://img.kancloud.cn/fa/9a/fa9a78b50c286ce8847624214d3f01b5_644x376.png)
点击`Postman`应用图标就可以打开应用。
如果是首次使用的话,会首先要求注册用户,完成后会进入主界面:
![](https://img.kancloud.cn/4f/2d/4f2dd98026038529641efe6e85f6b40a_1258x651.png)
下面我们就来测试下前面的`REST`应用的接口,测试之前选择对应的请求类型,并输入我们的接口地址,如果需要传入参数,则点击`Send`按钮之前的`Params`,依次输入`key`(参数名称)和`value`(参数值),然后点击`Send`按钮即可,下面依次测试博客的接口。
> #### 博客添加接口:post <http://tp5.com/blogs>
![](https://img.kancloud.cn/ef/57/ef576f89fb011b0ccdbfa25338f25c03_1258x699.png)
通过添加接口我们写入了两条数据。
> #### 博客读取接口:get <http://tp5.com/blogs/1>
![](https://img.kancloud.cn/bc/9b/bc9b803a441181ba7316a438d970a32c_1258x557.png)
> #### 博客更新接口:put <http://tp5.com/blogs/1>
![](https://img.kancloud.cn/07/3f/073f9f52c5afe4bc75bd47f61ef1e812_1261x564.png)
> #### 博客列表接口:get <http://tp5.com/blogs>
![](https://img.kancloud.cn/60/9a/609a77edf9cef0c484c4133b9fe8507c_1261x702.png)
> #### 删除博客接口:delete <http://tp5.com/blogs/1>
![](https://img.kancloud.cn/14/34/1434933c67e2ca129acce2bdbc9d589a_1255x475.png)
如果某个接口出现错误,可以点击`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扩展进行如下设置:
![](https://img.kancloud.cn/aa/7d/aa7db2472ddeca57d914df97f90590f8_293x351.png)
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`应用的远程调试信息了。
![](https://img.kancloud.cn/99/c2/99c2644e9937372e2c57141d37721c8b_967x231.png)
一旦有请求产生,就会自动刷新`Console`信息显示。
还可以支持异常和错误信息的记录,例如:
![](https://img.kancloud.cn/32/36/3236b520d879c3ec3faa471b61ecd8b9_979x151.png)
如果需要指定多个`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://ihavenolimitations.xyz/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碌脛脮媒脠路脳脣脢脝