wassapaks / laravel_api_boilerplate_firebase
这是一个Laravel API模板,您可以用它快速构建您的第一个API。基于Laravel 11框架构建。这是我为了我的一个项目开发的。这个API需要Firebase进行身份验证,但您可以配置使用JWT、Cognito、Sanctum或您自己的选择。因为大多数组件和框架都是开源的,所以我分享这个项目,以帮助有相同需求的人,欢迎提交PR进行改进和添加我遗漏的组件。
Requires
- php: ^8.2
- dedoc/scramble: ^0.11.4
- kreait/laravel-firebase: ^5.8
- laravel/framework: ^11.9
- laravel/sanctum: ^4.0
- laravel/tinker: ^2.9
- predis/predis: ^2.2
- sentry/sentry-laravel: ^4.7
- spatie/laravel-permission: ^6.9
- willdurand/hateoas: ^3.10
Requires (Dev)
- fakerphp/faker: ^1.23
- laravel/pint: ^1.13
- laravel/sail: ^1.26
- mockery/mockery: ^1.6
- nunomaduro/collision: ^8.0
- phpstan/phpstan: ^1.11
- phpunit/phpunit: ^11.0.1
This package is auto-updated.
Last update: 2024-09-07 13:03:47 UTC
README
这是一个Laravel API模板,您可以用它快速构建API。基于Laravel 11框架构建。这是我为我的个人项目开发的。这个API需要Firebase进行身份验证,但您可以选择使用JWT、Cognito、Sanctum或其他方式。因为这个项目的大部分组件和框架都是开源的,所以分享它以帮助有相同需求的人,欢迎提交PR进行改进和添加我遗漏的组件。
在构建这个API时,我考虑了以下REST组件清单
- Firebase身份验证
- 创建了一个基本的CRUD端点
- 用户管理
- 基于IP和用户使用Redis集成速率限制器
- 使用Redis的缓存示例
- 应用CSP、CORS和安全头部
- 使用头部进行版本控制
- 在日志监控中使用Sentry
- 将门和策略应用于API
- 应用仓库设计模式
- 应用可搜索的API文档
- 使用https://github.com/willdurand/Hateoas应用Hateoas
要求
在开始之前,您需要以下内容。
安装
请确保您已安装composer。
composer create-project wassapaks/laravel_api_boilerplate_firebase
然后执行php artisan migrate --seed以初始化表和填充表。
配置您的.env文件
要运行此项目,您需要将以下环境变量添加到您的.env文件中。
Firebase
下载您的Firebase身份验证json凭证,并将其放置在storage app firebase中
FIREBASE_CREDENTIALS=storage/app/firebase-auth.json FIREBASE_CONTINUE_URL="https://:8080"
Redis
REDIS_CLIENT=predis REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379
Sentry
SENTRY_LARAVEL_DSN= SENTRY_TRACES_SAMPLE_RATE=1.0 SENTRY_PROFILES_SAMPLE_RATE=1.0VITE_SENTRY_DSN_PUBLIC="${SENTRY_LARAVEL_DSN}"
版本头部
X_API_VERSION=v1
版本控制
在您的路由文件夹中,您将看到api.php和api.v2.php,这是我实现版本控制的方式,它在bootstrap/app.php的withrouting()中进行映射,当发起请求时。您需要在您的Postman请求中包含X_API_VERSION,其值应该是文件名的第二段。最初您的身份验证在api.v2.php上,Books CRUD示例在api.php上,在您的头部应该是X_API_VERSION: v1。
要添加新版本,只需创建新文件,例如:api.v3.php
以下是bootstrap/app.php中版本控制实现的示例。
->withRouting( commands: __DIR__ . '/../routes/console.php', health: '/up', using: function (Request $request) { $apiVersion = $request->header('X-Api-Version', env('X_API_VERSION')); $routeFile = $apiVersion != env('X_API_VERSION') ? base_path(sprintf('routes/%s.%s.php', 'api', $apiVersion)) : base_path(sprintf('routes/%s.php', 'api')); if (!file_exists($routeFile)) { return response()->json([ 'error_message' => 'Wrong Version Request.', ], 400); } Route::middleware('api') ->prefix('api') ->group($routeFile); },)
在您的请求中添加以下头部:X_API_VERSION:
Firebase身份验证和用户路由
我已经将身份验证和路由放置在api.v2.php中。要测试,请将请求头中的X_API_VERSION设置为v2。
POST api/auth/signin请求令牌GET api/auth/verify验证令牌POST api/user-management/users创建用户和Firebase用户GET api/user-management/users/{$id}显示单个用户DELETE api/user-management/users/{$id}删除用户GET api/user-management/users显示所有用户
CORS配置
Laravel 11 已经可以响应 CORS
<?php return [ /* |-------------------------------------------------------------------------- | Cross-Origin Resource Sharing (CORS) Configuration |-------------------------------------------------------------------------- | | Here you may configure your settings for cross-origin resource sharing | or "CORS". This determines what cross-origin operations may execute | in web browsers. You are free to adjust these settings as needed. | | To learn more: https://mdn.org.cn/en-US/docs/Web/HTTP/CORS | */ 'paths' => ['api/*', 'sanctum/csrf-cookie'], 'allowed_methods' => ['POST','GET', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'], 'allowed_origins' => ['*'], 'allowed_origins_patterns' => [], 'allowed_headers' => ['Origin', 'X-Api-Version','Authorization', 'Content-Type', 'Accept'], 'exposed_headers' => [], 'max_age' => 0, 'supports_credentials' => false, ];
安全头和CSP
您可以在 app/Http/Middleware/SecurityHeadersMiddleware.php 中修改安全头。
// For Reference: // https://cheatsheetseries.owasp.ac.cn/cheatsheets/REST_Security_Cheat_Sheet.html#security-headers $response->headers->set('Content-Security-Policy', "default-src 'none'; frame-ancestors 'none'"); $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); $response->headers->set('X-Content-Type-Options', 'no-sniff'); $response->headers->set('Content-Type', 'application/json'); $response->headers->set('X-Frame-Options:', 'DENY'); $response->headers->set('Referrer-Policy:', 'no-referrer'); $response->headers->set('Permissions-Policy', 'aaccelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()'); $response->header('X-Api-Version', $request->header('X-Api-Version', 'v1')); $response->header('X-Api-Latest-Version', config('app.latestApiVersion'));
图书路由(CRUD示例)
routes/api.php
Route::middleware(['firebase.auth', 'throttle:api'])->group(function () { Route::get('/books', [BookController::class, 'index']); Route::get('/books/{id}', [BookController::class, 'show']); Route::post('/books', [BookController::class, 'store']); Route::put('/books/{id}', [BookController::class, 'update']); Route::delete('/books/{id}', [BookController::class, 'destroy']); });
以下是事情发生的方式
app/Http/Controllers/BookController.php处理您的输入和输出app/Interfaces/BookRepositoryInterface.php在app/Providers/RepositoryServiceProvider.php中与app/Repositories/BookRepository.php绑定,处理您的资源数据处理app/Http/Resources/BookResource.php处理资源数据的返回,您可以在其中添加Hateoasapp/Http/Requests/StoreBookRequest.php、app/Http/Requests/UpdateBookRequest处理验证和用户策略
创建路由
- 您可以使用现有版本或在路由目录中添加新的
api.[new version].php - 在您的API中,您可以指定身份验证和节流中间件
Route::middleware(['firebase.auth', 'throttle:api'])->group(function ()
速率限制器
- 速率限制器可以在
app/Http/Providers/RouteServiceProvider.php中进行配置
public function boot(): void { RateLimiter::for('api', function (Request $request) { $limit = ($request->bearerToken()) ? 1000 : 50; return Limit::perMinute($limit)->by($request->ip())->response(function (Request $request, array $headers) { return ApiResponseClass::tooManyRequest($request->ip()); }); }); }
- 或者您可以在控制器的特定端点放置特定的速率限制器,例如
public function login(Request $request): ApiResponseClass { if (RateLimiter::tooManyAttempts('login-attempt:'.$request->ip(), $perMinute = 5)) { return ApiResponseClass::tooManyRequest($request->ip()); } RateLimiter::increment('login-attempt:'.$request->ip()); }
Hateoas
使用 HATEOAS 库。您可以查看库文档以获取更多详细信息。
用法
- 将您的超媒体放在
app/Hateoas/目录下。您可以使用注释、XML、Yaml。我使用的示例是使用注释。
示例
<?php declare(strict_types=1); namespace App\Hateoas; use Hateoas\Configuration\Annotation as Hateoas; use JMS\Serializer\Annotation as Serializer; /** * @Hateoas\Relation("self", href = "expr('/api/books/' ~ object.getId())") */ class Books { /** @Serializer\XmlAttribute */ private $id; public function __construct($id) { $this->id = $id; } public function getId() { return $this->id; } }
- 在您的请求类中,您可以添加Hateos并将其合并到响应数据中。
public function toArray(Request $request): array { $data = [ 'id' => $this->id, 'name' => $this->name, 'author' => $this->author, 'publish_date' => $this->publish_date, ]; $links = new HateoasClass(new Books($this->id)); return $links->getLinks() ? array_merge($data, $links->getLinks()) : $data; }
反馈
如果您有任何改进建议,请随时提出建议或打开PR!