使用 Dingo API 快速构建 RESTful API

# 安装
$ 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 () {
    ...
}]);

你可能感兴趣的:(使用 Dingo API 快速构建 RESTful API)