sgalinski / sg-rest
该扩展提供了一个基本的REST环境。新端点提供了REST环境,这样其他扩展只需要注册它们即可。
Requires
- typo3/cms-core: ^12.4.0
Replaces
- sgalinski/sg_rest: 6.0.0
- dev-master
- 6.0.0
- v5.x-dev
- 5.0.13
- 5.0.12
- 5.0.11
- 5.0.10
- 5.0.9
- 5.0.8
- 5.0.7
- 5.0.6
- 5.0.5
- 5.0.4
- 5.0.3
- 5.0.2
- 5.0.1
- 5.0.0
- 4.3.4
- 4.3.3
- 4.3.2
- 4.3.1
- 4.3.0
- 4.2.1
- 4.2.0
- 4.1.3
- 4.1.2
- 4.1.1
- 4.1.0
- 4.0.5
- 4.0.4
- 4.0.3
- 4.0.2
- 4.0.1
- 4.0.0
- 2.0.1
- 2.0.0
- 1.8.1
- 1.8.0
- 1.7.0
- 1.6.2
- 1.6.1
- 1.6.0
- 1.5.3
- 1.5.2
- 1.5.1
- 1.5.0
- 1.4.0
- 1.3.0
- 1.2.0
- 1.1.0
- 1.0.9
- 1.0.8
- 1.0.7
- 1.0.6
- 1.0.5
- 1.0.4
- 1.0.3
- 1.0.2
- 1.0.0
This package is not auto-updated.
Last update: 2024-09-16 19:00:35 UTC
README
许可协议: GNU GPL, 版本 2
仓库: https://gitlab.sgalinski.de/typo3/sg_rest
请在此处报告错误: https://gitlab.sgalinski.de/typo3/sg_rest
如何调用REST函数?
使用Chrome和Postman
在这种情况下,您可以安装Chrome扩展程序"Postman"(https://www.getpostman.com/)。使用此扩展程序,您可以向特定URL发送带有POST参数的REST调用,这是我们的REST实现所必需的。
REST注册
目标
在此注册过程之后,您可以调用以下REST函数
Calls an action of the entity, or returns an entity with the given uid.
URL: https://www.website-base.dev/?type=1595576052&request=<apiKey>/<entityName>/<actionOrUid>
Required POST data:
authToken = <aAuthTokenFromAUser> // See "REST Authentication" for this
OR
bearerToken = <bearerToken> // See "REST Authentication" for this
任务
1) 在扩展的"ext_localconf.php"中调用此函数
$class = 'SGalinski\SgRest\Service\RegistrationService';
$restRegistrationService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance($class);
$restRegistrationService->registerAccessGroup(
<apiKey>, // Example: "news"
'Vendor.<extension_key>', // Example: "Vendor.sg_news"
<accessGroupName>, // Example: "News" It's the name of the api, which is shown in the user TCA. See: "REST Authentication"
[
<entityName> => [ // Example: "news"
'read' => 'uid, title' // This allows that the API can read the fields "uid" and "title" from the entity "news"
]
]
);
2) 创建一个控制器,它是注册的端点
namespace Vendor\ExtensionName\Controller\Rest\<apiKeyWithCamelcase>; // Example: "...\Controller\Rest\News"
use SGalinski\SgRest\Controller\AbstractRestController;
use SGalinski\SgRest\Service\PaginationService;
use SGalinski\SgRest\Domain\Model\FrontendUser;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings;
class <entityNameWithCamelcase>Controller extends AbstractRestController { // Example: "NewsController"
/**
* Class name for the model mapping
*
* @var string
*/
protected $className = <entityNameWithNameSpace>; // Example: "Vendor\ExtensionName\Domain\Model\News"
/**
* @var EntityRepository
*/
protected $entityRepository;
/**
* Injects the repository. Is lot faster to use the inject method than the inject annotation!
*
* @param EntityRepository $entityRepository
* @return void
*/
public function injectEntityRepository(EntityRepository $entityRepository) {
/** @var $querySettings Typo3QuerySettings */
$querySettings = GeneralUtility::makeInstance('TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings');
$querySettings->setRespectStoragePage(FALSE);
$entityRepository->setDefaultQuerySettings($querySettings);
$this->entityRepository = $entityRepository;
}
/**
* Action to return an entity in json format.
*
* @param Entity $entity
* @return void
* @throws \Exception
*/
public function getAction(Entity $entity) {
if (!$entity) {
throw new \InvalidArgumentException('You´re not allowed to access this entity!', 403);
}
$this->returnData($this->dataResolveService->getArrayFromObject($entity));
}
/**
* Get list request for entities.
*
* @param int $page
* @param int $limit
* @throws \Exception
* @return void
*/
public function getListAction($page = 1, $limit = 10) {
/** @var FrontendUser $authenticatedUser */
$authenticatedUser = $this->authenticationService->getAuthenticatedUser();
if (!$authenticatedUser) {
throw new \InvalidArgumentException('You´re not allowed to access this entity list!', 403);
}
$response = ['amountOfAllEntries' => 0, 'prev' => NULL, 'next' => NULL, 'data' => []];
$amountOfAllEntities = $this->entityRepository->countAll();
/** @var PaginationService $paginationService */
$class = 'SGalinski\SgRest\Service\PaginationService';
$paginationService = GeneralUtility::makeInstance($class, $page, $limit, 10, $amountOfAllEntities, $this->apiKey);
$paginationSettings = $paginationService->getPaginationSettings();
$response['prev'] = $paginationService->getPreviousPageUrl(<entityName>); // Example: "news"
$response['next'] = $paginationService->getNextPageUrl(<entityName>); // Example: "news"
$response['amountOfAllEntries'] = $amountOfAllEntities;
$entries = $this->entityRepository->findAllWithPagination($limit, $paginationSettings['offset']);
foreach ($entries as $entry) {
$response['data'][] = $this->dataResolveService->getArrayFromObject($entry);
}
$this->returnData($response);
}
}
3) 在您的实体仓库中创建函数"findAllWithPagination"
/**
* Find all entries with some pagination information.
*
* @param int $limit
* @param int $offset
* @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
*/
public function findAllWithPagination($limit, $offset) {
return $this->createQuery()->setOrderings(['crdate' => QueryInterface::ORDER_ASCENDING])
->setOffset((int) $offset)
->setLimit((int) $limit)
->execute();
}
额外的注册配置
如果您想使用POST/PUT/PATCH/DELETE请求,则需要添加额外的配置"httpPermissions"。
$class = 'SGalinski\SgRest\Service\RegistrationService';
$restRegistrationService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance($class);
$restRegistrationService->registerAccessGroup(
<apiKey>, // Example: "news"
'Vendor.<extension_key>', // Example: "Vendor.sg_news"
<accessGroupName>, // Example: "News" It's the name of the api, which is shown in the user TCA. See: "REST Authentication"
[
<entityName> => [ // Example: "news"
'classFQN' => Vendor\ExtensionName\Controller\Rest\<apiKeyWithCamelcase>\<entityNameWithCamelcase>Controller::class,
'read' => 'uid, title' // This allows that the API can read the fields "uid" and "title" from the entity "news",
'httpPermissions' => [
'deleteForVerbs' => TRUE,
'putWithIdentifier' => TRUE,
'patchWithIdentifier' => TRUE,
'postWithIdentifier' => TRUE,
],
]
]
);
提示
- 在您的控制器函数中始终使用函数"$this->returnData"来返回数据。
- 始终使用函数"$this->dataResolveService->getArrayFromObject($entity)"从您注册的实体获取数据。这尊重在"$restRegistrationService->registerAccessGroup()"中配置的允许字段。
- 在每个api函数中应使用缓存。
- 尽量不使用Extbase调用,这样性能会更好。
REST认证
我们的REST解决方案与前端用户的授权一起工作。为此,它为这项任务提供了两个新的TCA字段。
认证令牌字段
此字段必须设置为唯一的ID,因此可以与提供的"authToken"参数映射,该参数必须在每次REST函数调用中设置。
访问组字段
列出所有可用的注册访问组。每个组都与一个REST api相关联。如果用户在此处设置了组,则用户可以访问API。
使用Bearer Token代替认证令牌
作为认证令牌的替代方案,我们的REST解决方案还提供了通过Bearer Token / JWT进行认证。认证成功后,用户会获得一个令牌,然后必须在每次请求中发送该令牌。如果令牌有效,服务器将执行所需请求。由于此类令牌可以在无需数据库连接的情况下进行验证,因此当在认证服务/服务器可能成为瓶颈的微服务环境中使用时,它特别有趣。
使用BearerAuthenticationService
为了实际使用Bearer Token进行认证,我们需要将BasicAuthenticationService的使用切换到BearerAuthenticationService。使用AuthenticationService的类通过AuthenticationServiceInterface的构造函数依赖注入获得它,这意味着任何实现此接口的AuthenticationService都可以注入此处。默认情况下,为该接口配置的Services.yaml中的别名是BasicAuthenticationService。要切换到使用BearerAuthenticationService,您需要更改您的Services.yaml中的此别名
默认
SGalinski\SgRest\Service\Authentication\AuthenticationServiceInterface: '@SGalinski\SgRest\Service\Authentication\BasicAuthenticationService'
要使用BearerAuthenticationService
SGalinski\SgRest\Service\Authentication\AuthenticationServiceInterface: '@SGalinski\SgRest\Service\Authentication\BearerAuthenticationService'
如何获取Bearer Token
要获取Bearer Token,您需要执行一个身份验证请求,并将有效的用户凭据传输到以下URL
/?type=1595576052&tx_sgrest[request]=authentication/authentication/getBearerToken&logintype=login
由于我们使用基于中间件TYPO3\CMS\Frontend\Middleware\FrontendUserAuthenticator
的标准身份验证过程,因此请求必须以POST方式执行,并包含表单数据参数user
和pass
。
当用户身份验证成功,并且用户至少被允许使用一个REST端点时,将返回Bearer Token
{
"bearerToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoyLCJleHAiOjE2MDgwMjQzMTd9.exQ3UkeBBd2P_1o4woP0uBn6koxvTV63aVT13JTkWRw"
}
实现自己的AuthenticationService
您也可以实现自己的AuthenticationService。为此,您需要构建一个类,该类可以实现AuthenticationServiceInterface接口或扩展AbtstractAuthenticationService。
namespace Vendor\Extension\Service\Authentication;
use TYPO3\CMS\Core\SingletonInterface;
use SGalinski\SgRest\Service\AbstractAuthenticationService;
class CustomAuthenticationService extends AbstractAuthenticationService implements SingletonInterface {
/**
* @param array $requestHeaders
* @return bool
* @throws Exception
*/
public function verifyRequest(array $requestHeaders): bool {
// verify Request
//return bool;
}
/**
* Verify if the authenticated user has access to the given apikey.
*
* @param $apiKey
* @return bool
*/
public function verifyUserAccess($apiKey): bool {
// verify user access
//return bool;
}
}
附加配置
更好的调用URL
带有API子域
Old: https://www.website-base.dev/?type=1595576052&tx_sgrest[request]=<apiKey>/<entityName>/<actionOrUid>
New: https://api.website-base.dev/<apiKey>/<entityName>/<actionOrUid>
如果您想使用上述类似URL调用REST api,那么您只需将以下代码添加到项目的 .htaccess 文件中
# Api redirects
RewriteCond %{HTTP_HOST} ^api.(?:website-base.dev?)$ [NC]
RewriteRule ^(.+)$ index.php?type=1595576052&tx_sgrest[request]=%{REQUEST_URI} [QSA,NC,L]
不带API子域
Old: https://www.website-base.dev/?type=1595576052&tx_sgrest[request]=<apiKey>/<entityName>/<actionOrUid>
New: https://www.website-base.dev/api/v1/<apiKey>/<entityName>/<actionOrUid>
如果您想使用上述类似URL调用REST api,那么您需要将以下代码添加到 .htaccess 文件中
# Api redirects
RewriteRule ^api/v1/(.*) /index.php?type=1595576052&tx_sgrest[request]=$1 [QSA]
有关更多信息,请参阅下一节“自定义REST URL模式”
自定义REST URL模式
如果您的REST URL不是子域,您可能在主机后有一个URL段或完全不同。默认URL模式类似于HOST/APIKEY/ENTITY,如果您的宿主不包含API的标识符,您需要调整REST URL模式。否则,您的分页服务将生成无效的下一个和上一个URL。
您不需要添加URL方案。HTTPS是必需的,并且始终使用。URL模式使用handlebars进行标记。以下标记存在
- {{HOST}}
- {{APIKEY}}
- {{ENTITY}}
因此,默认模式是:{{HOST}}/{{APIKEY}}/{{ENTITY}}
如果您想将URL从https://api.yourdomain.com/apikey/entity调整为https://www.yourdomain.com/api/apikey/entity等,您需要将新的URL模式设置为分页服务。
/** @var PaginationService $paginationService */
$paginationService = GeneralUtility::makeInstance(
PaginationService::class, $page, $limit, 10, $amountOfEntries, $apiKey
);
$paginationService->setUrlPattern('{{HOST}}/api/{{APIKEY}}/{{ENTITY}}');
路由增强器
遗憾的是,由于PathUtility提供的映射到Rest Controller和Action的路径在请求参数中,因此该扩展的URL模式目前与路由增强器不兼容
[...]?sg_rest[request]=news/news/getList
路由增强器无法正确映射此内容。
在此期间,请使用上述解释的 .htaccess 选项美化您的REST URL!
日志记录和垃圾回收
此扩展使用典型的TYPO3日志机制。默认情况下,它使用数据库和表tx_sgrest_log。建议配置一个清理任务。如果您使用的是默认清除所有表的配置,则日志条目将每30天清除一次。