token验证
在API中一般都是使用token来验证身份,那如何验证token的有效性以及对应的用户呢? DingoApi 为我们准备好了 api.auth 这个中间件,
用来区分哪些接口需要验证 token,哪些不需要。
路由使用如下:1
2
3
4
5
6// 需要 token 验证的接口
$api->group(['middleware' => 'api.auth'], function($api) {
// 当前登录用户信息
$api->get('user', 'UsersController@me')
->name('api.user.show');
});
错误处理
vendor\dingo\api\src\Facade\API.php 中error函数可以处理异常,
比如在 AppServiceProvider 中的 register 注册 ,捕获一些特殊的异常情况1
2
3
4
5
6
7
8
9
10
11
12
13
14public function register()
{
.
.
// 模型没找到,返回404
\API::error(function (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
abort(404);
});
// 用户权限不对,返回403
\API::error(function (\Illuminate\Auth\Access\AuthorizationException $exception) {
abort(403, $exception->getMessage());
});
}
Fractal
Fractal 是一个转换层(transformer),API 开发中非常方便的一种开发方法,可以帮助我们处理响应数据的结构与复杂的嵌套关系,最后将数据返回给客户端。
可以把 Fractal 理解为 Web 开发中视图,控制着 API 的最终数据输出。Laravel 5.5 的新功能 eloquent-resources 整体思路跟 Fractal 一致,用法也基本相同。
DingoApi 已经安装了 Fractal,并且做了很多整合,基本解决了 N+1 问题。
Fractal 数据结构
- DataArraySerializer 结构类似 eloquent-resources 的 Data Wrapping
- ArraySerializer 结构类似 eloquent-resources 的 withoutWrapping
- JsonApiSerializer 结构出自 JSON-API,是一套 json 接口响应规范。
创建文件如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
namespace App\Transformers;
use App\Models\User;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
public function transform(User $user)
{
return [
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
'avatar' => $user->avatar,
'introduction' => $user->introduction,
'bound_phone' => $user->phone ? true : false,
'bound_wechat' => ($user->weixin_unionid || $user->weixin_openid) ? true : false,
'last_actived_at' => $user->last_actived_at->toDateTimeString(),
'created_at' => $user->created_at->toDateTimeString(),
'updated_at' => $user->updated_at->toDateTimeString(),
];
}
}
在 UsersController 调用如下:1
2
3
4public function me()
{
return $this->response->item($this->user(), new UserTransformer());
}
dingo-serializer-switch 中间件
Fractal可以为我们创建结构统一的相应数据,我们可以使用 dingo-serializer-switch 中间件切换两种数据结构1
composer require liyu/dingo-serializer-switch
路由修改1
2
3
4$api->version('v1', [
'namespace' => 'App\Http\Controllers\Api',
'middleware' => 'serializer:array'
], function ($api) {
Include 机制
我们经常需要返回额外的资源数据,并且设置数据的结构。此时可以使用include机制1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class TopicTransformer extends TransformerAbstract
{
protected $availableIncludes = ['user', 'category'];
public function transform(Topic $topic)
{
return [
...
];
}
public function includeUser(Topic $topic)
{
return $this->item($topic->user, new UserTransformer());
}
public function includeCategory(Topic $topic)
{
return $this->item($topic->category, new CategoryTransformer());
}
}
- 1.在 $availableIncludes 中添加 可以嵌套的额外资源
- 2.额外资源如何获取,通过 includeUser 和 includeCategory。
- 3.方法命名规则 include + user 、 include + category 驼峰命名。
客户端提交的include参数指定,多个参数通过逗号分隔。1
http://{{host}}/api/topics?include=user,category
此处include参数中 可以 是逗号 或者 点:
- 逗号 —— 是当前资源所关联的资源,如 include=topic,user;
- 点 —— 当前资源所关联的资源,及其所关联的资源,相当于下一级资源,如 include=topic.user
注意:
在 Transformer 中,我们可以使用:
- $this->item() 返回单个资源
- $this->collection() 返回集合资源
Include 让资源与资源之间以一种合理的嵌套返回,同时什么时候返回完全由请求参数决定,这让资源数据更加灵活
N+1问题以及查询日志
当我们需要查询是否含有 N+1 问题时,需要输出SQL查询日志,可以使用 laravel-query-logger 组件1
composer require overtrue/laravel-query-logger --dev
然后,再次调用接口,再运行1
tail -f ./storage/logs/laravel.log
DingoApi 根据 include 参数以及 defaultInclude 帮助我们进行预加载,所以大部分情况我们不需要手动处理。如果遇到复杂的嵌套及关系加载,可以添加1
app(\Dingo\Api\Transformer\Factory::class)->disableEagerLoading();
临时关闭 DingoApi 的预加载,手动处理。