# 安装
$ composer require dingo/api:3.0.x@dev
# 1.在 api.php 中编写接口
# 2.在响应构建器中使用转化器构建JSON 响应
# 1.
$api = app(\Dingo\Api\Routing\Router::class); # 获取对应的 API 路由器实例
$api->version('v1', function ($api) {
//
}); # 版本分组
# .env中:
API_SUBTYPE= # 子类型,默认为空
API_PREFIX=dingoapi # 配置路由前缀
API_DOMAIN=api.myapp.com # 子域名,和前缀二选一进行配置
API_VERSION=v1 # 版本
API_NAME= # 名字
API_CONDITIONAL_REQUEST=false # 带条件的请求
API_STRICT=false # Strict 模式
# 完整的 Accept 字段值格式
# Accept: application/API_STANDARDS_TREE.API_SUBTYPE.API_VERSION+json
# 2.
# app\Http\Controller 下创建新的 API 基类控制器
php artisan make:controller ApiController
# 内容:
class ApiController extends Controller
{
use Helpers;
}
# 定义一个继承自该控制器的子控制器
php artisan make:controller Api/TaskController --resource
# 返回 JSON 响应示例:
$task = Task::findOrFail($id);
return $this->response->array($task->toArray());
# API 路由示例:
$api->version('v3', function ($api) {
$api->resource('tasks', \App\Http\Controllers\Api\TaskController::class);
});
# 三种序列化器
$fractal->setSerializer(new \League\Fractal\Serializer\ArraySerializer());
$fractal->setSerializer(new \League\Fractal\Serializer\DataArraySerializer()); # 默认
$fractal->setSerializer(new \League\Fractal\Serializer\JsonApiSerializer());
# 转化器
# 新建 app/Transformers/TaskTransformer 转化器,初始化代码:
namespace App\Transformers;
use App\Task;
use League\Fractal\TransformerAbstract;
class TaskTransformer extends TransformerAbstract
{
public function transform(Task $task)
{
return [
'id' => $task->id,
'text' => $task->text,
'completed' => $task->is_completed ? 'yes' : 'no',
'link' => route('tasks.show', ['id' => $task->id])
];
}
}
# 单个资源:
$task = Task::findOrFail($id);
return $this->response->item($task, new TaskTransformer());
# 资源集合:
$tasks = Task::all();
return $this->response->collection($tasks, new TaskTransformer());
# 添加额外的响应头
return $this->response->item($task, new TaskTransformer)->withHeader('Foo', 'Bar');
# 添加 cookie, 必须要是实例
$cookie = new \Symfony\Component\HttpFoundation\Cookie('foo', 'bar');
...->withCookie($cookie);
# 设置响应状态码
...->setStatusCode(200);
# 添加元数据
...->addMeta('foo', 'bar');
API 认证
HTTP基本认证
# 1.注册驱动,在 AuthServiceProvider 的 boot 方法中注册基本认证
# 2.在相应的 API 路由中认证中间件
# 3.访问 (添加Basic Auth / 不添加)
# 这里的认证信息是首页注册时的原邮箱和原密码
# 1.
use Dingo\Api\Auth\Auth;
use Dingo\Api\Auth\Provider\Basic;
public function boot()
{
// Dingo 认证驱动注册
$this->app->make(Auth::class)->extend('basic', function ($app) {
return new Basic($app['auth'], 'email');
});
}
# 2.
$api->version('v3', ['middleware' => 'api.auth'], function ($api) {
$api->resource('tasks', \App\Http\Controllers\Api\TaskController::class);
});
# or,在指定路由上应用该中间件
$api->version('v3', function ($api) {
$api->resource('tasks', \App\Http\Controllers\Api\TaskController::class, [
'middleware' => 'api.auth'
]);
});
JWT认证
# 准备工作:
$ composer require tymon/jwt-auth
$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
$ php artisan jwt:secret
# 让用于认证的 User 模型类实现 JWTSubject 接口
class User extends Authenticatable implements JWTSubject {
public function getJWTIdentifier()
{
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}
}
# 1.注册驱动,可以在 config/api.php 中注册对应的认证驱动,也可以和 HTTP 一样
# 2.获取 JWT 令牌
# 3.基于 JWT 认证访问 API
# 1. config/api/php:
'auth' => [
'jwt' => \Dingo\Api\Auth\Provider\JWT::class,
],
# 2.
$api->version('v3', function ($api) {
$api->post('user/auth', function () {
$credentials = app('request')->only('email', 'password');
try {
if (! $token = \Tymon\JWTAuth\Facades\JWTAuth::attempt($credentials)) {
throw new \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException('Invalid credentials');
}
} catch (\Tymon\JWTAuth\Exceptions\JWTException $e) {
throw new \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException('Create token failed');
}
return compact('token');
});
}); # 访问即可获取到 Token 值
# 3.代码保持和 HTTP 基本认证一样,设置认证类型为 Bearer Token,写入获取到的 Token 值
OAuth 2.0 认证
1.安装配置 Passport
$ composer require laravel/passport # 安装
# php artisan migrate # 生成用于存放客户端和访问令牌的数据表
$ php artisan passport:install # 创建生成安全访问令牌(token)所需的加密键
# 添加 Laravel\Passport\HasApiTokens trait 到 App\User 模型
namespace App;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}
# 在 AuthServiceProvider 的 boot 方法中调用 Passport::routes 方法
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
];
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
}
# 修改配置文件 config/auth.php
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
2.设置认证中间件
# Api\TaskController.php:
public function __construct()
{
$this->middleware('auth:api');
}
3.通过密码授权令牌访问认证 API
# 1.获取CLIENT_ID和CLIENT_SECRET,配置到 .env 中
# 2.在 routes/api.php 中定义一个用于获取授权令牌的路由
# 3.将获取到的access_token(认证 API 时的令牌)设置到类型为 Bearer 的 Authorization 字段,请求 tasks.index 路由
# 1.
$ php artisan passport:client --password
# 2.
$api->version('v3', function ($api) {
$api->post('user/token', function () {
app('request')->validate([
'email' => 'required|string',
'password' => 'required|string',
]);
$http = new \GuzzleHttp\Client();
// 发送相关字段到后端应用获取授权令牌
$response = $http->post(route('passport.token'), [
'form_params' => [
'grant_type' => 'password',
'client_id' => env('CLIENT_ID'),
'client_secret' => env('CLIENT_SECRET'),
'username' => app('request')->input('email'), // 这里传递的是邮箱
'password' => app('request')->input('password'), // 传递密码信息
'scope' => '*'
],
]);
return response()->json($response->getBody()->getContents());
});
$api->resource('tasks', \App\Http\Controllers\Api\TaskController::class);
});
php 内置的 http server 是 block 的,一次只能处理一个请求,这里将项目部署到wamp中。
访问频率限制
通过 Laravel 自带的节流中间件 - throttle
'throttle:60,1', # 默认1 分钟内可以对应用该中间件的路由发起 60 次请求
$api->post('user/token', ['middleware' => 'throttle:3,1', function () {
//
}]); # 通过传递参数到该中间件来手动设置时间范围和请求次数
# 响应头中通过 X-RateLimit-* 字段获取频率限制相关数值:
X-RateLimit-Limit: 3
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1557801344
通过 Dingo 实现的节流中间件
$api->post('user/token', ['middleware' => 'api.throttle', 'limit' => 3, 'expires' => 1, function () {
//
}
自定义节流器
# 创建自定义节流器 app/Throttles/CustomThrottle.php:
namespace App\Throttles;
use Dingo\Api\Contract\Http\RateLimit\HasRateLimiter;
use Dingo\Api\Http\RateLimit\Throttle\Throttle;
use Dingo\Api\Http\Request;
use Illuminate\Container\Container;
class CustomThrottle extends Throttle implements HasRateLimiter
{
protected $options = ['limit' => 5, 'expires' => 1];
public function match(Container $container)
{
return ! $container['api.auth']->check();
}
// 通过域名+IP识别客户端
public function getRateLimiter(Container $app, Request $request)
{
return $request->route()->getDomain() . '|' . $request->getClientIp();
}
}
# 在 routes/api.php 中通过 throttle 参数指定 user/token 路由使用该自定义节流器
$api->post('user/token', ['middleware' => 'api.throttle', 'throttle' => 'App\Throttles\CustomThrottle', function () {
...
}]);