laragear / api-manager
管理多个REST服务器,以少量代码流畅地发起请求。
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.5
- illuminate/config: 10.*|11.*
- illuminate/container: 10.*|11.*
- illuminate/http: 10.*|11.*
- illuminate/support: 10.*|11.*
Requires (Dev)
- orchestra/testbench: 8.*|9.*
README
管理多个REST服务器,以少量代码流畅地发起请求。不再需要冗长的HTTP请求!
use App\Http\Apis\Chirper; $chirp = Chirper::api()->chirp('Hello world!');
成为赞助商
您的支持使我能够保持这个包免费、更新和可维护。或者,您可以 传播信息!
要求
- Laravel 10 或更高版本
安装
使用Composer在项目中要求此内容
composer require laragear/api-manager
用法
创建API服务器
要使用API服务器,定义一个继承自 Laragear\ApiManager\ApiServer
的类。您可以使用 make:api
Artisan 命令在 app\Http\Apis
目录中创建一个现成的存根。
php artisan make:api Chirper
您将收到一个包含基础URL和操作、以及添加一些头部信息和访问令牌空间的文件。您可以自由地调整以满足您的需求。
namespace App\Http\Apis; use Laragear\ApiManager\ApiServer; class Chirper extends ApiServer { /** * The headers to include in each request. * * @var array{string:string}|array */ public $headers = [ // ... ]; /** * The list of simple actions for this API. * * @var array|string[] */ public $actions = [ 'latest' => '/', 'create' => 'post:new', ]; /** * Returns the API base URL. * * @return string */ public function getBaseUrl() { return app()->isProduction() ? 'https://chirper.com/api/v1' : 'https://dev.chirper.com/api/v1'; } /** * Returns the Bearer Token used for authentication. * * @return string */ protected function authToken() { return config('services.chirper.secret'); } }
注意
您可以在 stubs/api.stub
中创建一个API服务器存根来覆盖API服务器存根。
内联操作
在API类中设置操作解决了拥有多个端点并在应用程序中每次都准备每个端点的问题,这可能导致错误或充满文本的复杂函数。
定义操作的最简单方法是使用 $actions
数组,使用语法 verb:route/{parameters}
,其中键是您稍后要调用的操作名称。如果您没有定义动词,将推断为 get
。
/** * The list of simple actions for this API. * * @var array|string[] */ protected $actions = [ 'new chirp' => 'post:new', 'latest' => 'latest', 'view' => 'chirp/{id}', 'edit' => 'update:chirp/{id}', 'delete' => 'delete:chirp/{id}', ];
例如,要更新一个 chirp,我们可以直接从我们的 ChirpApi
调用 edit
。
在此过程中,请手动添加 PHPDoc 到您的API服务器,以便利用自动完成(智能感知)。
use Laragear\ApiManager\ApiServer; /** * @method \Illuminate\Http\Client\Response newChirp($data = []) * @property-read \Illuminate\Http\Client\Response $latest * @property-read \Illuminate\Http\Client\Response $view * @method \Illuminate\Http\Client\Response edit($data = []) */ class Chirper extends ApiServer { // ... }
然后,以 camelCase 表示法调用操作名称。参数将传递到HTTP请求中。
use App\Http\Apis\Chirper; // Create a new chirp. $chirp = Chirper::api()->newChirp(['message' => 'This should be complex']);
如果路由有命名参数,您可以在调用服务器时将它们作为参数设置。
use App\Http\Apis\Chirper; // Edit a chirp. Chirper::api(['id' => 231])->edit(['message' => 'No, it was a breeze!']); // Same as: Chirper::api('chirper')->withUrlParameters(['id' => 231])->edit(['message' => 'No, it was a breeze!']);
此外,您还可以像属性一样调用不带参数的操作。
use App\Http\Apis\Chirper; $latestChirps = Chirper::api()->latest;
方法操作
对于更复杂的情况,您可以使用类方法。只需确保在需要自定义请求的任何参数上对 PendingRequest
进行类型提示即可。
use Illuminate\Http\Client\PendingRequest; public function newChirp(PendingRequest $request, string $message) { return $request->connectTimeout(10)->post('new', ['message' => $message]); } public function noReply(PendingRequest $request) { $request->withHeaders(['X-No-Reply' => 'false']) return $this; }
然后,您可以像任何周一早晨一样调用类方法。
use App\Http\Apis\Chirper; $chirp = Chirper::api()->newChirp('Easy peasy');
注意
方法操作优先于内联操作。
与内联操作一样,方法操作也可以作为属性执行,如果这些操作不需要参数。
use App\Http\Apis\Chirper; $latest = Chirper::api()->noReply->newChirp('Easy peasy');
身份验证
API服务器支持Laravel HTTP客户端的 三种身份验证类型:基本、摘要和Bearer令牌。您可以使用 authBasic()
或 authDigest()
定义每种身份验证的数组形式的用户名和密码,并使用 authToken()
定义令牌。
/** * Returns the Basic Authentication to use against the API. * * @var array{string:string}|void */ public function authBasic() { return app()->isProduction() ? ['app@chirper.com', 'real-password'] : ['dev@chirper.com', 'fake-password']; }
警告
不要使用关联数组来匹配底层方法。由于Laravel 不保证命名参数的一致性,您应该选择简单的数组。
// This is supported, but discouraged! return ['username' => 'app@chirper', 'password' => 'real-password'];
在构建请求之前和之后
您可以选择在请求启动前后修改请求,分别使用beforeBuild()
和afterBuild()
。在beforeBuild()
中,PendingRequest
实例接收到基本URL后执行,而在添加头信息和认证后,调用afterBuild()
。
use Illuminate\Http\Client\PendingRequest; public function beforeBuild(PendingRequest $request) { // } public function afterBuild(PendingRequest $request) { // }
在这里,您可以自由地访问请求实例并对其进行修改,适用于所有端点,或者返回一个全新的PendingRequest
实例。
提示
如果您正在使用之前版本的旧build()
方法,它仍然有效,因为beforeBuild()
将调用build()
。
重写请求
API请求可以像往常一样重写。如果这些方法不存在于API类中,所有方法都会传递给Illuminate\Http\Client\PendingRequest
实例。
use App\Http\Apis\Chirper; $chirp = Chirper::api()->timeout(5)->latest();
注意
如果该方法存在于您的API类中,它将具有优先权。
依赖注入
所有API服务器都是通过服务容器解决的,因此您可以通过构造函数添加任何您需要的服务到对象中。
use Illuminate\Filesystem\Filesystem; use Laragear\ApiManager\ApiServer; class Chirper extends ApiServer { public function __construct(protected Filesystem $file) { if ($this->file->missing('important_file.txt')) { throw new RuntimeException('Important file missing!') } } // ... }
如果您需要更深入地自定义API服务器,也可以在AppServiceProvider
中创建一个回调来解决问题。
// app\Providers\AppServiceProvider.php use App\Http\Apis\Chirper; public function register() { $this->app->bind(Chirper::class, function () { return new Chirper(config('services.chirper.version')); }) }
并发请求
要向池中添加API服务器请求,请为每个并发请求使用onPool()
方法。不需要将所有请求发送到同一个API服务器,因为您可以混合和匹配不同的目的地。
use Illuminate\Support\Facades\Http; use App\Http\Apis\Chirper; use App\Http\Apis\Twitter; $responses = Http::pool(fn ($pool) => [ Chirper::api()->on($pool)->chirp('Hello world!'), Twitter::api()->on($pool)->tweet('Goodbye world!'), $pool->post('mastodon.org/api', ['message' => 'Greetings citizens!']) ]); return $responses[0]->ok();
您还可以使用on()
方法的第二个参数对请求进行命名。
use Illuminate\Support\Facades\Http; use App\Http\Apis\Chirper; use App\Http\Apis\Twitter; $responses = Http::pool(fn ($pool) => [ Chirper::api()->on($pool, 'first')->chirp('Hello world!'), Twitter::api()->on($pool, 'second')->tweet('Goodbye world!'), $pool->as('third')->post('mastodon.org/api', ['message' => 'Greetings citizens!']) ]); return $responses['first']->ok();
包装到自定义响应中
您可能会收到响应,并需要手动将数据映射到自己的类中。而不是手动处理,您可以将传入的响应自动包装到自定义的"API响应"中。
首先,使用make:api-response
创建一个用于api的自定义响应,您想要使用的API,以及将自定义响应命名为与端点相同的名称。理想情况下,您可能希望将其命名为您计划使用的操作或方法的名称。
php artisan make:api-response Chirper ViewResponse
您将收到如下文件
namespace App\Http\Apis\Chirper\Responses; use Illuminate\Http\Client\Response; class ViewResponse extends Response { // }
注意
您可以通过在stubs/api-response.stub
中创建一个来覆盖API响应存根。
在这个类中,您可以创建任何您想要的方法。由于您的类将扩展基础Laravel HTTP客户端 Response
类,您将访问其所有方便的方法。
public function isPrivate(): bool { return $this->json('metadata.is_private', false) }
一旦完成自定义API响应的定制,您可以使用您的Api类的$responses
数组将其映射到操作和方法。
/** * Actions and methods to wrap into a custom response class. * * @var array<string, class-string> */ protected $responses = [ 'view' => Responses\ViewResponse::class, ];
这将使API管理器在每次调用该方法以接收响应时将其包装到您的自定义响应中。例如,如果您调用view()
,您将收到一个新的ViewResponse
实例。
use App\Http\Apis\Chirper; $chirp = Chirper::api(['id' => 5])->view(); if ($chirp->successful() && $chirp->isPrivate()) { return 'The chirp cannot be seen publicly.'; }
提示
当使用async()
请求时,自定义响应将在解析后自动包装。
测试
您可以使用HTTP外观的
轻松测试API服务器操作是否正常工作。fake()
方法
use Illuminate\Support\Facades\Http; use Illuminate\Http\Client\Request; public function test_creates_new_chirp(): void { Http::fake(function (Request $request) { return Http::response([ 'posted' => 'ok', ...json_decode($request->body()) ], 200); }); $this->post('spread-message', ['message' => 'Hello world!']) ->assertSee('Posted!'); }
Laravel Octane 兼容性
- 没有使用过时的应用程序实例的单例。
- 没有使用过时的配置实例的单例。
- 没有使用过时的请求实例的单例。
- 没有写入静态属性。
使用此包与Laravel Octane不应有任何问题。
安全
如果您发现任何与安全相关的漏洞,请通过电子邮件darkghosthunter@gmail.com报告,而不是使用问题跟踪器。
许可证
此特定包版本根据发布时的MIT许可证许可。
Laravel是Taylor Otwell的商标。版权所有 © 2011-2024 Laravel LLC。