Wallogit.com
2017 © Pedro Peláez
think-api 是给开发者提供的一套针对thinkphp的API扩展工具,帮助开发者方便快捷的建造自己的API应用。, (*2)
该包是针对thinkphp5.1以上版本, (*3)
这个包提供了以下等工具: - API版本管理 - 响应生成器 - 数据过滤器 - JWT(Json Web Token)的支持, (*4)
thinp-api 是给开发者提供的一套API工具,帮助你方便快捷的建造你自己的API,有什么使用问题和反馈建议请在issue中提出, (*5)
另外,欢迎Star和Fork该项目😄😄😄😄😄, (*6)
安装该扩展包需要环境支持, (*7)
修改你的 composer.json 文件,然后执行 composer update 把最后一个版本的包加入你的项目, (*8)
"require": {
"zewail/think-api": "1.1.*"
}
或者你可以在命令行执行 composer require 命令, (*9)
composer require zewail/think-api:1.1.x
如果你使用了自定义路由,则可以使用路由版本管理, (*10)
使用think-api的版本管理方法来创建版本, (*11)
$api = new \Zewail\Api\Routing\Router;
$api->version('v1', function () {
// TODO 可以是thinkphp自带的路由
});
或者使用门面(Facede), (*12)
use Zewail\Api\Facades\ApiRoute;
ApiRoute::version('v1', function(){
// TODO 可以是thinkphp自带的路由
});
你想一个分组返回多个版本,只需要传递一个版本数组, (*13)
ApiRoute::version(['v1', 'v2'], function () {
// TODO 可以是thinkphp自带的路由
});
ApiRoute::version('v1', function(){
Route::rule('new/:id','index/News/read');
});
因为每个版本分组了,你可以为相同 URL 上的同一个路由创建不同响应, (*14)
ApiRoute::version('v1', function () {
Route::rule('new/:id','app\index\controller\V2\News@read');
// 或者
Route::rule('new/:id','index/V2.News/read');
});
ApiRoute::version('v2', function () {
Route::rule('new/:id','app\index\controller\V2\News@read');
});
默认访问配置文件中的默认版本, (*15)
但是,我们可以在Http的头信息中附带Api-Version参数,或者直接在在url或body中附带version参数来访问指定版本, (*16)
http://example.com/new/102?version=v2
响应生成器提供了一个流畅的接口去方便的建立一个定制化的响应, (*17)
要利用响应生成器, 你的控制器需要使用Zewail\Api\Api trait, 可以建立一个通用控制器,然后你的所有的 API 控制器都继承它。, (*18)
namespace app\index\controller;
use think\Controller;
use Zewail\Api\Api;
class BaseController extends Controller
{
use Api;
}
然后你的控制器可以直接继承基础控制器。响应生成器可以在控制器里通过 $response 属性获取。, (*19)
当然,也可以使用门面(Facade)来获取, (*20)
namespace app\index\controller;
use Zewail\Api\Facades\Response as ApiResponse;
class IndexController
{
public function index() {
return ApiResponse::array([]);
}
}
// 简单的成功响应, 默认200状态码, 可以在第二个参数改变
// 使用 trait, 其他方法都可以使用该方法,下面都使用 Facade 演示
return $this->response->array($user->toArray());
// 使用 Facade
return ApiResponse::success('Success', 200);
$user = User::get($id); return ApiResponse::array($user->toArray());
$user = User::get($id); return ApiResponse::item($user);
$users = User::all(); return ApiResponse::collection($users);
$users = User::paginate(10); return ApiResponse::paginator($users);
捕获错误响应需要接管系统的异常处理机制,将系统config/app.php中的 exception_handle配置为Zewail\Api\Exceptions\handleException, (*21)
// 异常处理handle类 留空使用 \think\exception\Handle 'exception_handle' => 'Zewail\Api\Exceptions\handleException',
返回一个错误响应, (*22)
return ApiResponse::BadRequest();
当然也可以返回成功的响应, (*23)
return ApiResponse::OK($data);
| 方法名 | 状态码 | 说明 |
|---|---|---|
| Continue | 100 | Continue |
| SwitchingProtocols | 101 | Switching Protocols |
| Processing | 102 | Processing |
| EarlyHints | 103 | Early Hints |
| OK | 200 | OK |
| Created | 201 | Created |
| Accepted | 202 | Accepted |
| NonAuthoritativeInformation | 203 | Non-Authoritative Information |
| NoContent | 204 | No Content |
| ResetContent | 205 | Reset Content |
| PartialContent | 206 | Partial Content |
| MultiStatus | 207 | Multi-Status |
| AlreadyReported | 208 | Already Reported |
| IMUsed | 226 | IM Used |
| MultipleChoices | 300 | Multiple Choices |
| MovedPermanently | 301 | Moved Permanently |
| Found | 302 | Found |
| SeeOther | 303 | See Other |
| NotModified | 304 | Not Modified |
| UseProxy | 305 | Use Proxy |
| TemporaryRedirect | 307 | Temporary Redirect |
| PermanentRedirect | 308 | Permanent Redirect |
| BadRequest | 400 | Bad Request |
| Unauthorized | 401 | Unauthorized |
| PaymentRequired | 402 | Payment Required |
| Forbidden | 403 | Forbidden |
| NotFound | 404 | Not Found |
| MethodNotAllowed | 405 | Method Not Allowed |
| NotAcceptable | 406 | Not Acceptable |
| ProxyAuthenticationRequired | 407 | Proxy Authentication Required |
| RequestTimeou | 408 | Request Timeou |
| Conflict | 409 | Conflict |
| Gone | 410 | Gone |
| LengthRequired | 411 | Length Required |
| PreconditionFailed | 412 | Precondition Failed |
| PayloadTooLarge | 413 | Payload Too Large |
| URITooLong | 414 | URI Too Long |
| UnsupportedMediaType | 415 | Unsupported Media Type |
| RangeNotSatisfiable | 416 | Range Not Satisfiable |
| ExpectationFailed | 417 | Expectation Failed |
| IAmATeapot | 418 | I\'m a teapot |
| MisdirectedRequest | 421 | Misdirected Request |
| UnprocessableEntity | 422 | Unprocessable Entity |
| Locked | 423 | Locked |
| FailedDependency | 424 | Failed Dependency |
| UnorderedCollection | 425 | Unordered Collection |
| UpgradeRequired | 426 | Upgrade Required |
| PreconditionRequired | 428 | Precondition Required |
| TooManyRequests | 429 | Too Many Requests |
| RequestHeaderFieldsTooLarge | 431 | Request Header Fields Too Large |
| UnavailableForLegalReasons | 451 | Unavailable For Legal Reasons |
| InternalServerError | 500 | Internal Server Error |
| NotImplemented | 501 | Not Implemented |
| BadGateway | 502 | Bad Gateway |
| ServiceUnavailable | 503 | Service Unavailable |
| GatewayTimeout | 504 | Gateway Timeout |
| HTTPVersionNotSupported | 505 | HTTP Version Not Supported |
| VariantAlsoNegotiates | 506 | Variant Also Negotiates |
| InsufficientStorage | 507 | Insufficient Storage |
| LoopDetected | 508 | Loop Detected |
| NotExtended | 510 | Not Extended |
| NetworkAuthenticationRequired | 511 | Network Authentication Required |
return ApiResponse::item($user)->addMeta('foo', 'bar');
或者直接设置 Meta 数据的数组, (*24)
return ApiResponse::item($user)->setMeta($meta);
return ApiResponse::item($user)->setCode(200);
// 提供两种设置方式
return ApiResponse::item($user)->addHeader('X-Foo', 'Bar');
return ApiResponse::item($user)->addHeader(['X-Foo' => 'Bar']);
return ApiResponse::item($user)->setLastModified($time);
return ApiResponse::item($user)->setETag($eTag);
return ApiResponse::item($user)->setExpires($time);
return ApiResponse::item($user)->setCacheControl($cache);
其中item collection paginator具有两个参数,第一个参数为模型数据,第二个参数为数据过滤列表, (*25)
// 如查询出来的$user具有id, name, age, mobile等属性 // 在设置了第二个参数为['id', 'name', 'age']后,将会过滤其他属性,只返回给接口列出的属性 return ApiResponse::item($user, ['id', 'name', 'age']); return ApiResponse::collection($users, ['id', 'name', 'age']); return ApiResponse::paginator($users, ['id', 'name', 'age']);
或者通过only与except方法过滤数据, (*26)
// 只选择模型中的id、name、age属性 return ApiResponse::only(['id', 'name', 'age'])->item($user); // 排除模型属性age return ApiResponse::except(['age'])->item($user); // 还可以一起使用, 选择id、name、age属性后排除age return ApiResponse::only(['id', 'name', 'age'])->except(['age'])->item($user);
提供了一个配置文件用于数据过滤或者说是数据资源的集中管理, (*27)
使用该功能需要在thinkphp中新建一个配置文件resources.php, (*28)
return [ // 用户相关接口 // 例如设置一些用户的相关接口资源 'user.age' => ['id', 'name', 'age'], 'user.mobile' => ['id', 'name', 'mobile'], ];
然后在返回接口数据的时候在item collection paginator第二个参数传入该标识即可, (*29)
// 返回{'data': {'id':1, 'name': 'xiaoming', 'age': 20}}
return ApiResponse::item($user, 'user.age');
// 返回{'data': {'id':1, 'name': 'xiaoming', 'mobile': '13777777777'}}
return ApiResponse::item($user, 'user.mobile');
或者通过only与except方法, (*30)
// 返回{'data': {'id':1, 'name': 'xiaoming', 'age': 20}}
return ApiResponse::only('user.age')->item($user);
// 返回{'data': {'id':1, 'name': 'xiaoming', 'mobile': '13777777777'}}
return ApiResponse::only('user.mobile')->item($user);
item、collection、paginator的第二个过滤参数属性,会覆盖only与except方法, (*31)
如果默认配置Array,想返回DataArray格式的数据,可以:, (*32)
// 返回Array格式的数据
return ApiResponse::item($user)->serializer('Array');
// 返回DataArray格式的数据
return ApiResponse::item($user)->serializer('DataArray');
JWT相关知识大家百度一下吧,网上很多,直接上代码, (*33)
使用 JWT 门面的 attempt 方法来自动验证, (*34)
namespace app\index\controller;
use app\index\model\User;
use Zewail\Api\Facades\Response;
use Zewail\Api\Facades\JWT;
use Zewail\Api\Exceptions\JWTException;
class Authenticate
{
public function authenticate()
{
// $credentials 可以从请求中获取
$credentials = ['email'=>'chanzewail@gmail.com', 'password' => '123456'];
$token = JWT::attempt($credentials);
}
}
这里使用了 email 和 password 来验证用户是否合法,如果你的用户是通过 mobile或其它字段作为标识,那么可以在 app\index\model\User 模型中,添加 jwtSub 字段:, (*35)
namespace app\index\model;
use think\Model;
class User extends Model
{
public $jwtSub = 'mobile';
}
当然,如果你的密码 不是用的 password (绝大多数都用这个,不排除少数奇怪的命名….),那么你可以添加 jwtPassword 字段:, (*36)
public $jwtPassword = 'strange_password';
这里验证psssword默认使用md5加密,绝大多数情况下这是不够安全的,很多都有自定义的加密方式,那么还有验证密码的方法,添加:, (*37)
public function jwtEncrypt($password)
{
// 只要返回你加密后的结果,会自动比对数据库字段
return md5(sha1($password));
}
还可以直接通过用户对象实例创建token, (*38)
$user = User::get(1); $token = JWT::fromUser($user);
还可以自定义 Payload 创建任意数据, (*39)
$customClaims = ['foo' => 'bar', 'baz' => 'bob']; $payload = JWT::makePayload($customClaims); $token = JWT::encode($payload);
要通过http发送一个需要认证通过的请求,需要设置Authorization头, (*40)
Authorization: Bearer {token}
或者将token信息包含到URL中, (*41)
http://api.example.com/news?token={token}
resolveToken方法可以将token还原为payload数组, (*42)
$payload = JWT::resolveToken(); // ['foo' => 'bar', 'baz' => 'bob']
如果是从user模型创建的token,那么还可以使用authenticate方法,直接验证用户,成功后返回用户模型,失败返回false, (*43)
if ($user = JWT::authenticate()) {
// todo
}
当然还有更加手动的方法, (*44)
// 从请求中获取token $token = JWT::getToken(); // todo // 对token进行解码 $payload = JWT::decode($token);
该扩展包共有3个配置文件, (*45)
配置文件仅支持全局配置目录使用, (*46)
api.php:管理接口配置resources.php:过滤管理器配置jwt.php:JWT相关配置配置文件可以在vendor/zewail/think-api/config目录下找到,也可以手动创建它们, (*47)
retrun [
//配置项
];
api的默认版本, (*48)
api返回的数据格式,可选:, (*49)
DataArray:数组带data格式,默认值, (*50)
// item
{
'data': [...],
'meta': [...],
...
}
// collection
{
'data': [
{...},
{...},
{...},
],
'meta': [...],
...
}
Array:数组格式, (*51)
// item
{
'item_field1': '...',
'item_field2': '...',
'meta': [...],
...
}
// collection
{
[
{...},
{...},
],
'meta': [...],
...
}
, (*52)
retrun [ //配置项 // 用户相关接口 // 例如设置一些用户的相关接口资源 'user.age' => ['id', 'name', 'age'], 'user.mobile' => ['id', 'name', 'mobile'], ];
该配置文件用于数据过滤管理,在返回接口数据的时候在item collection paginator第二个参数传入该标识来使用, (*53)
// 返回{'data': {'id':1, 'name': 'xiaoming', 'age': 20}}
return $this->response->item($user, 'user.age');
// 返回{'data': {'id':1, 'name': 'xiaoming', 'mobile': '13777777777'}}
return $this->response->item($user, 'user.mobile');
retrun [
//配置项
];
token的过期时间, 默认为120分钟,单位分钟, (*54)
允许误差时间,默认为60秒,单位秒, (*55)
加密算法,支持:, (*56)
HS256: HMAC 使用 SHA-256 算法加密
HS512: HMAC 使用 SHA-512 算法加密
RS256: RSA 使用 SHA-256 算法加密
如果使用了HMAC加密方式,则需要配置该项,为自定义字符串, (*57)
如果使用了RSA加密方式,则需要配置该项,为.pem结尾的私钥文件路径, (*58)
如果使用了RSA加密方式,则需要配置该项,为.pem结尾的公钥文件路径, (*59)
如果需要使用用户操作相关方法,则需定义该项,为用户模型所在路径,如, (*60)
'user' => app\index\model\User::class,
MIT license, (*61)
response::array报错的问题