zewail/think-api

v1.1.3 2020-05-07 10:11 UTC

README

Latest Stable Version Total Downloads Latest Unstable Version License

think-api 是给开发者提供的一套针对thinkphp的API扩展工具,帮助开发者方便快捷的建造自己的API应用。

该包是针对thinkphp5.1以上版本

简介

这个包提供了以下等工具:

  • API版本管理
  • 响应生成器
  • 数据过滤器
  • JWT(Json Web Token)的支持

thinp-api 是给开发者提供的一套API工具,帮助你方便快捷的建造你自己的API,有什么使用问题和反馈建议请在issue中提出

另外,欢迎Star和Fork该项目😄😄😄😄😄

安装

安装该扩展包需要环境支持

  • thinkphp 5.1+
  • php 5.6+

修改你的 composer.json 文件,然后执行 composer update 把最后一个版本的包加入你的项目

"require": {
    "zewail/think-api": "1.1.*"
}

或者你可以在命令行执行 composer require 命令

composer require zewail/think-api:1.1.x

路由

如果你使用了自定义路由,则可以使用路由版本管理

版本组

使用think-api的版本管理方法来创建版本

$api = new \Zewail\Api\Routing\Router;

$api->version('v1', function () {
	// TODO 可以是thinkphp自带的路由
});

或者使用门面(Facede)

use Zewail\Api\Facades\ApiRoute;

ApiRoute::version('v1', function(){
    // TODO 可以是thinkphp自带的路由
});

你想一个分组返回多个版本,只需要传递一个版本数组

ApiRoute::version(['v1', 'v2'], function () {
	// TODO 可以是thinkphp自带的路由
});
创建路由
ApiRoute::version('v1', function(){
    Route::rule('new/:id','index/News/read');
});

因为每个版本分组了,你可以为相同 URL 上的同一个路由创建不同响应

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');
});
访问特定路由版本

默认访问配置文件中的默认版本

但是,我们可以在Http的头信息中附带Api-Version参数,或者直接在在url或body中附带version参数来访问指定版本

http://example.com/new/102?version=v2

响应

响应生成器

响应生成器提供了一个流畅的接口去方便的建立一个定制化的响应

要利用响应生成器, 你的控制器需要使用Zewail\Api\Api trait, 可以建立一个通用控制器,然后你的所有的 API 控制器都继承它。

namespace app\index\controller;

use think\Controller;
use Zewail\Api\Api;

class BaseController extends Controller
{
	use Api;
}

然后你的控制器可以直接继承基础控制器。响应生成器可以在控制器里通过 $response 属性获取。

当然,也可以使用门面(Facade)来获取

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

// 异常处理handle类 留空使用 \think\exception\Handle
'exception_handle'       => 'Zewail\Api\Exceptions\handleException',

返回一个错误响应

return ApiResponse::BadRequest();

当然也可以返回成功的响应

return ApiResponse::OK($data);

添加其他响应数据

添加 Meta 数据
return ApiResponse::item($user)->addMeta('foo', 'bar');

或者直接设置 Meta 数据的数组

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']);
设置 LastModified
return ApiResponse::item($user)->setLastModified($time);
设置 ETag
return ApiResponse::item($user)->setETag($eTag);
设置 Expires
return ApiResponse::item($user)->setExpires($time);
页面缓存控制
return ApiResponse::item($user)->setCacheControl($cache);

响应数据过滤

其中item collection paginator具有两个参数,第一个参数为模型数据,第二个参数为数据过滤列表

// 如查询出来的$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']);

或者通过onlyexcept方法过滤数据

// 只选择模型中的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);
集中管理

提供了一个配置文件用于数据过滤或者说是数据资源的集中管理

使用该功能需要在thinkphp中新建一个配置文件resources.php

return [
  // 用户相关接口
  // 例如设置一些用户的相关接口资源
  'user.age' => ['id', 'name', 'age'],
  'user.mobile' => ['id', 'name', 'mobile'],
];

然后在返回接口数据的时候在item collection paginator第二个参数传入该标识即可

// 返回{'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');

或者通过onlyexcept方法

// 返回{'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方法

设置serializer

如果默认配置Array,想返回DataArray格式的数据,可以:

// 返回Array格式的数据
return ApiResponse::item($user)->serializer('Array');
// 返回DataArray格式的数据
return ApiResponse::item($user)->serializer('DataArray');

JWT

JWT相关知识大家百度一下吧,网上很多,直接上代码

创建Token

使用 JWT 门面的 attempt 方法来自动验证

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 字段:

namespace app\index\model;

use think\Model;

class User extends Model
{
	public $jwtSub = 'mobile';
}

当然,如果你的密码 不是用的 password (绝大多数都用这个,不排除少数奇怪的命名….),那么你可以添加 jwtPassword 字段:

public $jwtPassword = 'strange_password';

这里验证psssword默认使用md5加密,绝大多数情况下这是不够安全的,很多都有自定义的加密方式,那么还有验证密码的方法,添加:

public function jwtEncrypt($password)
{
 	// 只要返回你加密后的结果,会自动比对数据库字段
  	return md5(sha1($password));
}

还可以直接通过用户对象实例创建token

$user = User::get(1);
$token = JWT::fromUser($user);

还可以自定义 Payload 创建任意数据

$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
$payload = JWT::makePayload($customClaims);
$token = JWT::encode($payload);

用户认证

要通过http发送一个需要认证通过的请求,需要设置Authorization头

Authorization: Bearer {token}

或者将token信息包含到URL中

http://api.example.com/news?token={token}
解析token

resolveToken方法可以将token还原为payload数组

$payload = JWT::resolveToken();
// ['foo' => 'bar', 'baz' => 'bob']

如果是从user模型创建的token,那么还可以使用authenticate方法,直接验证用户,成功后返回用户模型,失败返回false

if ($user = JWT::authenticate()) {
    // todo
}

当然还有更加手动的方法

// 从请求中获取token
$token = JWT::getToken();

// todo

// 对token进行解码
$payload = JWT::decode($token);

配置

该扩展包共有3个配置文件

配置文件仅支持全局配置目录使用

  • api.php:管理接口配置
  • resources.php:过滤管理器配置
  • jwt.php:JWT相关配置

配置文件可以在vendor/zewail/think-api/config目录下找到,也可以手动创建它们

api.php

retrun [
    //配置项
];
version

api的默认版本

serializer

api返回的数据格式,可选:

  • DataArray:数组带data格式,默认值

    // item
    {
      'data': [...],
      'meta': [...],
      ...
    }
    
    // collection
    {
      'data': [
          {...},
          {...},
          {...},
        ],
      'meta': [...],
      ...
    }
  • Array:数组格式

    // item
    {
      'item_field1': '...',
      'item_field2': '...',
      'meta': [...],
      ...
    }
    
    // collection
    {
      [
      	{...},
        {...},
      ],
      'meta': [...],
      ...
    } 

resources.php

retrun [
  //配置项
  // 用户相关接口
  // 例如设置一些用户的相关接口资源
  'user.age' => ['id', 'name', 'age'],
  'user.mobile' => ['id', 'name', 'mobile'],
];

该配置文件用于数据过滤管理,在返回接口数据的时候在item collection paginator第二个参数传入该标识来使用

// 返回{'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');

jwt.php

retrun [
    //配置项
];
ttl

token的过期时间, 默认为120分钟,单位分钟

deviation

允许误差时间,默认为60秒,单位秒

algorithm

加密算法,支持:

  • HS256: HMAC 使用 SHA-256 算法加密
  • HS512: HMAC 使用 SHA-512 算法加密
  • RS256: RSA 使用 SHA-256 算法加密
key

如果使用了HMAC加密方式,则需要配置该项,为自定义字符串

privateKeyPath

如果使用了RSA加密方式,则需要配置该项,为.pem结尾的私钥文件路径

publicKeyPath

如果使用了RSA加密方式,则需要配置该项,为.pem结尾的公钥文件路径

user

如果需要使用用户操作相关方法,则需定义该项,为用户模型所在路径,如

'user' => app\index\model\User::class,

授权协议

MIT license

CHANGELOG

1.1.0-beta1

  • 修复 php7 以下环境调用response::array报错的问题
  • 添加全新状态码别名方法调用
  • 移除动词方法别名调用生成Http异常(现在统一使用状态码别名)