# 安裝
$ composer require dingo/api:3.0.x@dev
# 1.在 api.php 中編寫接口
# 2.在響應構(gòu)建器中使用轉(zhuǎn)化器構(gòu)建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 下創(chuàng)建新的 API 基類控制器
php artisan make:controller ApiController
# 內(nèi)容:
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());
# 轉(zhuǎn)化器
# 新建 app/Transformers/TaskTransformer 轉(zhuǎn)化器,初始化代碼:
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);
# 設置響應狀態(tài)碼
...->setStatusCode(200);
# 添加元數(shù)據(jù)
...->addMeta('foo', 'bar');
API 認證
HTTP基本認證
# 1.注冊驅(qū)動,在 AuthServiceProvider 的 boot 方法中注冊基本認證
# 2.在相應的 API 路由中認證中間件
# 3.訪問 (添加Basic Auth / 不添加)
# 這里的認證信息是首頁注冊時的原郵箱和原密碼
# 1.
use Dingo\Api\Auth\Auth;
use Dingo\Api\Auth\Provider\Basic;
public function boot()
{
// Dingo 認證驅(qū)動注冊
$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 模型類實現(xiàn) JWTSubject 接口
class User extends Authenticatable implements JWTSubject {
public function getJWTIdentifier()
{
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}
}
# 1.注冊驅(qū)動,可以在 config/api.php 中注冊對應的認證驅(qū)動,也可以和 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 # 生成用于存放客戶端和訪問令牌的數(shù)據(jù)表
$ php artisan passport:install # 創(chuàng)建生成安全訪問令牌(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 方法中調(diào)用 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.通過密碼授權(quán)令牌訪問認證 API
# 1.獲取CLIENT_ID和CLIENT_SECRET,配置到 .env 中
# 2.在 routes/api.php 中定義一個用于獲取授權(quán)令牌的路由
# 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();
// 發(fā)送相關(guān)字段到后端應用獲取授權(quán)令牌
$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 內(nèi)置的 http server 是 block 的,一次只能處理一個請求,這里將項目部署到wamp中。
訪問頻率限制
通過 Laravel 自帶的節(jié)流中間件 - throttle
'throttle:60,1', # 默認1 分鐘內(nèi)可以對應用該中間件的路由發(fā)起 60 次請求
$api->post('user/token', ['middleware' => 'throttle:3,1', function () {
//
}]); # 通過傳遞參數(shù)到該中間件來手動設置時間范圍和請求次數(shù)
# 響應頭中通過 X-RateLimit-* 字段獲取頻率限制相關(guān)數(shù)值:
X-RateLimit-Limit: 3
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1557801344
通過 Dingo 實現(xiàn)的節(jié)流中間件
$api->post('user/token', ['middleware' => 'api.throttle', 'limit' => 3, 'expires' => 1, function () {
//
}
自定義節(jié)流器
# 創(chuàng)建自定義節(jié)流器 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 參數(shù)指定 user/token 路由使用該自定義節(jié)流器
$api->post('user/token', ['middleware' => 'api.throttle', 'throttle' => 'App\Throttles\CustomThrottle', function () {
...
}]);