fullscreeninteractive / silverstripe-restful-helpers
Requires
Requires (Dev)
- phpunit/phpunit: ^9.5
- squizlabs/php_codesniffer: ^3
README
为基本的RESTful JSON API提供一些基础功能的模块。虽然大家似乎都在转向GraphQL,但如果您只需要一个快速设置,配置简单,那么简单的REST解决方案就无可匹敌。
处理认证并提供服务于解析API请求的常用功能。与silverstripe-restfulserver
相比,此模块默认不提供太多模型和字段的构建脚手架,而是依赖于开发者为API布局设计(尽管提供了构建脚手架助手)。
安装
composer require fullscreeninteractive/silverstripe-restful-helpers
使用
如果您计划为API使用认证,那么首先需要配置https://github.com/Level51/silverstripe-jwt-utils/模块。
app/_config/api.yml
Level51\JWTUtils\JWTUtils: secret: 'replace-this-with-a-jwt-secret-for-jwt' lifetime_in_days: 365 renew_threshold_in_minutes: 60
下一步是设置API的路由。您可以按项目需求修改路由的名称。至少您应该有一个特定于项目的端点,该端点将子类化ApiController
,例如MyProjectsApi
。
app/_config/routes.yml
SilverStripe\Control\Director: rules: 'api/v1/auth/$Action': 'FullscreenInteractive\Restful\Controllers\AuthController' 'api/v1/projects//$Action': 'MyProjectsApi'
以下是一个MyProjectsApi
的示例,展示了该模块提供的一些助手。任何人都可以通过GET api/v1/projects/
检索所有项目的列表,登录的ADMIN用户可以通过POST api/v1/projects/create
创建项目。
app/src/Project.php
<?php use FullscreenInteractive\Restful\Interfaces\ApiReadable; use SilverStripe\Security\Member; use SilverStripe\ORM\DataObject; class Project extends DataObject implements ApiReadable { private static $db = [ 'Title' => 'Varchar(100)', 'Date' => 'DBDate' ]; private static $has_one = [ 'Author' => Member::class ]; public function toApi(): array { return [ 'title' => $this->Title, 'date' => $this->dbObject('Date')->getTimestamp() ]; } }
app/src/MyProjectsApi.php
<?php class MyProjectsApi extends FullscreenInteractive\Restful\Controllers\ApiController { private static $allowed_actions = [ 'index', 'createProject', 'deleteProject' ]; public function index() { $this->ensureGET(); return $this->returnPaginated(Project::get()); } public function createProject() { $this->ensurePOST(); $member = $this->ensureUserLoggedIn([ 'ADMIN' ]); list($title, $date) = $this->ensureVars([ 'Title', 'Date' => function($value) { return strtotime($value) > 0 } ]); $project = new Project(); $project->Title = $title; $project->Date = $date; $project->AuthorID = $member->ID; $project->write(); return $this->returnJSON([ 'project' => $project->toApi() ]); } public function deleteProject() { $this->ensurePOST(); $member = $this->ensureUserLoggedIn([ 'ADMIN' ]); list($id) = $this->ensureVars([ 'id' ]); $project = Project::get()->byID($id); if (!$project) { return $this->failure([ 'status_code' => 404, 'message' => 'Unknown project' ]); } if ($project->canDelete($member)) { $project->delete(); } return $this->success(); } }
认证
认证通过JWT进行管理,可以存储在客户端。要接收令牌,用户必须首先通过基本认证将用户名/密码交换过来,通过带有凭证的POST请求。通常这是一种javascript请求形式
fetch('/api/v1/auth/token', { method: "POST", headers: { "Accept": "application/json", "Content-Type": "application/json", "Cache-control": "no-cache", "Authorization": "Basic " + base64.encode(email + ":" + password), }, })
该请求的响应将是一个错误代码(> 200),或者如果用户名和密码正确,则是一个包含JWT的200响应。令牌和相关元数据可以安全地保存在客户端以便重复使用。
{ "token": "eyJ0eXAiOiJKV1QiL...", "member": { "id": 1, "email": "js@lvl51.de", "firstName": "Julian", "surname": "Scheuchenzuber" } }
如果用户的令牌无效或已过期,将返回一个401错误。要验证用户的令牌,请使用verify
端点,这将检查令牌并在必要时更新令牌。
fetch('/api/v1/auth/verify', { method: "GET", headers: { Accept: "application/json", "Content-Type": "application/json", Authorization: "Bearer " + token, }, })
然后可以使用该令牌作为Bearer
头签名API调用。
fetch('/api/v1/projects/createProject', { method: "POST", headers: { "Accept": "application/json", "Content-Type": "application/json", "Cache-control": "no-cache", Authorization: "Bearer " + token, }, })
通过silverstripe-apikeys进行认证
如果您更愿意使用API密钥而不是JWT令牌,可以使用https://github.com/sminnee/silverstripe-apikey并将其配置为特定路由的中间件。
SilverStripe\Core\Injector\Injector: ApiRouteMiddleware: class: SilverStripe\Control\Middleware\RequestHandlerMiddlewareAdapter properties: RequestHandler: '%$MyProjectApi' Middlewares: CustomMiddleware: '%$ApiKeyRequestMiddleware' MyProjectApi: class: MyProjectApi ApiKeyRequestMiddleware: class: Sminnee\ApiKey\ApiKeyRequestMiddleware SilverStripe\Control\Director: rules: api: Controller: '%$ApiRouteMiddleware'
默认情况下,silverstripe-apikey模块不会在未提供API密钥时抛出错误(但如果提供错误的密钥会抛出错误)。因此,短期内您最好检查并处理API密钥未提供的情况。
public function projects()
{
if (!$this->ensureUserLoggedIn()) {
return $this->failure(401);
}
// ..
}
UUIDs
在设计API时,您可能希望避免在响应中暴露内部ID。
要为对象添加UUID字段,请将以下扩展添加到您的模型中
private static $extensions = [ UuidableExtension::class ];
对象将在onBeforeWrite()
时生成UUID。
常见问题解答
当尝试调用我的api/endpoint时,我得到了一个301重定向
在Silverstripe 5中,默认启用了CanonicalURLMiddleware
以添加尾随斜杠。这可能在生产中引起问题,所以我们建议完全禁用任何API路由的此功能。
SilverStripe\Core\Injector\Injector: SilverStripe\Control\Middleware\CanonicalURLMiddleware: properties: enforceTrailingSlashConfigIgnorePaths: - 'api/'
API文档
待办事项,但规模不大。现在请参阅ApiController
。
许可证
BSD-3-Clause