icekson/remote-services

构建服务器API的远程服务

v0.9.1.1 2017-04-12 20:18 UTC

This package is not auto-updated.

Last update: 2024-09-14 18:20:43 UTC


README

Api services是以一种框架独立的方式开发的,可以与任何PHP框架一起工作;

用于派发的路由是/api/v:version/:serviceName/:actionName,其中

* version : version of api (for example v1)
* serviceName - service's name
* serviceAction - action's name

每个服务都应该实现\Api\Service\RemoteServiceInterface。为了确定应该派发哪些服务和动作,我使用了注解(使用Doctrine\Common\Annotations作为解析注解的引擎)。

示例

namespace Service;

use Api\Service\RemoteServiceInterface

/**
 * Class AdvertiserStatsService
 * @Service(name = "advertiser")
 */
class AdvertiserStatsService implements RemoteServiceInterface
{
    /**
     * @ServiceAction(name="GetOffers")
     * 
     *
     */
    public function getOffers()
    {
    }
}

为了方便,已实现抽象类Api\Service\BaseService,它已经实现了RemoteServiceInterface接口,可以用作服务的基类;

namespace Service;

use Api\Service\BaseService

/**
 * Class AdvertiserStatsService
 * @Service(name = "advertiser")
 */
class AdvertiserStatsService extends BaseService
{
    /**
     * @ServiceAction(name="GetOffers")
     * 
     *
     */
    public function getOffers()
    {
    }
}

顺便说一句,如果您想将您的服务隐藏起来(通过token授权、权限检查等),您必须实现两个额外的接口Api\Service\SecurityServiceInterface和Api\Service\SecurityOwnerPermissionInterface

下面是服务示例的完整版本

namespace Service;

use Api\Service\BaseService;
use Api\Service\SecurityOwnerPermissionInterface;
use Api\Service\SecurityServiceInterface;
use Api\Service\IdentityInterface;

/**
 * Class AdvertiserStatsService
 * @Service(name = "advertiser")
 */
class AdvertiserStatsService extends BaseService implements SecurityServiceInterface, SecurityOwnerPermissionInterface
{
    /**
     * @ServiceAction(name="GetOffers")
     * 
     *
     */
    public function getOffers()
    {
    }

     public function isPermitted($token){}

     /**
     * @param Properties $params
     * @return IdentityInterface|null
     * @throw NoTokenException
     */
     public function getIdentity(Properties $params = null){}

     public function checkOwnPermission();
}

如代码所示,服务的名称由相应的注解@Service(name = "advertiser")定义,动作的名称由@ServiceAction(name="GetOffers")定义,这与URL: /api/v1/advertiser/GetOffers相匹配

请求派发

工作流程如下

1 从路由中检索服务和动作的名称 2 创建新的\Api\Dispatcher实例 3 将包含服务实现的路径全部注册到派发器中 4 调用相应的服务

示例

$version = "1";
$serviceName = "advertiser";
$serviceAction = "GetOffers";
$params = []; // some GET or POST params
$responseBuilder = new \Api\Service\Response\JsonBuilder(); // also it can be XmlBuilder or HtmlBuilder
$sm = new \Zend\ServiceManager\ServiceManager($conf);

$dispatcher = new \Api\Dispatcher();
$dispatcher->registerServicesPath(API_ROOT . "v".$version . "/services/Service/"); // this folder contains implementations of Services
$jsonResp = $dispatcher->dispatch($serviceName, $serviceAction, $params, $responseBuilder, $sm);

权限设置

如果您的服务实现了Api\Service\SecurityServiceInterface,那么您应该在每次请求中发送access_token参数,您可以将所有token保存在数据库或其他地方。您必须在SecurityServiceInterface::isPermitted($token)方法中实现token的验证

基于角色的权限

角色和相关权限的列表放在文件api/config/permissions.php中。在这里我们应该描述角色。如果角色没有明确定义,则拒绝访问。可以定义适当的服务的动作(serviceName.serviceAction)以及服务中的所有动作(serviceName.*)的访问权限

您还可以使用角色的继承,通过在任意角色中使用键'extends'来列出父角色,在这种情况下,该角色将继承所有父角色的权限;

api/config/permissions.php的示例

return array(
   'roles' => array(
       'developer' => array(
           'permissions' => array(
               'advertiser.*'
           ),
           'extends' => 'test'
       ),
       'affiliate' => array(
           'permissions' => array(
               'test.*',
           )
       ),
       'admin' => array(
           'extends' => array(
               'publisher',
               'developer'
           )
       ),
       'test' => array(
           'permissions' => array(
               'test.GetGroupedData',
           )
       )
   )
);

列、筛选和分组的设置

对于任何动作,我们都可以使用可用的列、筛选和分组。这里我们同样使用注解

@AcceptableColumns() @AcceptableFilters() @AceptableGroupings()

一些示例

 // 1 example

    /**
     * @ServiceAction(name="GetTerritoryStatistics")
     *
     * @AcceptableColumns({
     *      AdvertiserFilter::FIELD_OFFER_NAME,
     *      AdvertiserFilter::FIELD_TERRITORY,
     *      AdvertiserFilter::FIELD_CLICKS,
     *      AdvertiserFilter::FIELD_CONVERSIONS,
     *      AdvertiserFilter::FIELD_CR,
     *      AdvertiserFilter::FIELD_CR_PERSENTS,
     *      AdvertiserFilter::FIELD_SPENT,
     *      AdvertiserFilter::FIELD_CURRENCY
     * })
     *
     * @AcceptableFilters({
     *      AdvertiserFilter::DATE_FROM,
     *      AdvertiserFilter::DATE_TO,
     *      AdvertiserFilter::FIELD_OFFER_ID,
     *      AdvertiserFilter::FIELD_OFFER_DETAILS_ID
     *
     * })
     *
     * @AcceptableGroupings({
     *      AdvertiserFilter::FIELD_CPA
     * })
     */
    public function getTerritoryStatistics()
    {}

// 2 example

    /**
     * @ServiceAction(name="GetDailyStatistics")
     * @AcceptableColumns({
     *      AdvertiserFilter::FIELD_OFFER_NAME,
     *      AdvertiserFilter::FIELD_DATE,
     *      AdvertiserFilter::FIELD_CLICKS,
     *      AdvertiserFilter::FIELD_CONVERSIONS,
     *      AdvertiserFilter::FIELD_CR,
     *      AdvertiserFilter::FIELD_CR_PERSENTS
     * })
     * @AcceptableColumns(role = "admin", extendDefault = true, value = {
     *      AdvertiserFilter::FIELD_BD_MANAGER_ID
     * })
     *
     * @AcceptableFilters({
     *      AdvertiserFilter::DATE_FROM,
     *      AdvertiserFilter::DATE_TO,
     *      AdvertiserFilter::FIELD_OFFER_ID
     * })
     */
    public function getDailyStatistics() {}

如我们所见,我们可以在同一个地方多次放置相同的注解(@AcceptableColumns),在这种情况下,将选择最合适的用户角色,默认情况下将选择没有角色的注解。同样,您可以使用'extendDefault = true',所有列都将从默认注解扩展

AcceptableColumns

列可以定义为字符串或数组

api/v1/someService/someAction?columns=all api/v1/someService/someAction?columns=name,id,date api/v1/someService/someAction?columns[]=name&columns[]=id&columns[]=date

如果columns参数为空,则使用'columns=all'。如果您给出不正确的列,您将收到一个包含可用列的错误列表;

AcceptableFilters

api/v1/someService/someAction?filters[date_from]=2011-01-01&filters[date_to]=2014-01-01

AcceptableGroupings

格式与列相同

api/v1/someService/someAction?group=name,id,date api/v1/someService/someAction?group[]=name&group[]=id&group[]=date