braune-digital/api-base-bundle

v1.1.0 2017-06-12 17:07 UTC

This package is auto-updated.

Last update: 2024-09-12 00:04:07 UTC


README

此Symfony-Bundle使用FOS Rest并提供基本的API功能

特性

  • BaseApiController:API控制器的基石
  • ApiKey认证:使用API令牌验证用户
  • 分页
  • 查询过滤:过滤列表(即将推出)
  • 模块访问:将API分割成模块并限制对特定用户角色的访问
  • 响应中的自定义配置:向特定响应添加自定义配置

需求

  • FOSRestBundle
  • WhiteOctoberPagerFantaBundle
  • FOSUserBundle(目前)
  • JMSSerializerBundle(可选)

安装

使用composer下载

composer require braune-digital/api-base-bundle "1.*"

并在你的AppKernel中启用Bundle。

你也可以不注册Bundle就使用BaseApiController。

public function registerBundles()
    {
        $bundles = array(
          ...
            new JMS\SerializerBundle\JMSSerializerBundle(),
            new FOS\RestBundle\FOSRestBundle(),

            new BrauneDigital\ApiBaseBundle\BrauneDigitalApiBaseBundle(),
            new Nelmio\CorsBundle\NelmioCorsBundle(),
          ...
        );

配置

默认配置

braune_digital_api_base:
    modules: ~ #Used for Module-Access
    timeout: 0 # Timeout for Api-Tokens (use 0 for no timeout)
    configuration: # Your configuration to be send

FOSRest配置

fos_rest:
    disable_csrf_role: ROLE_API
    param_fetcher_listener: true
    body_listener:
        array_normalizer: fos_rest.normalizer.camel_keys
    format_listener: true
    view:
        view_response_listener: force
        exception_wrapper_handler: 'BrauneDigital\ApiBaseBundle\View\ExceptionWrapperHandler'
    routing_loader:
        default_format: json
    body_converter:
        enabled: true
        validate: true
    exception:
        codes:
            'Doctrine\ORM\EntityNotFoundException': 403
        messages:
            'Doctrine\ORM\EntityNotFoundException': false

NelmioCors配置

以支持来自客户端的OPTIONS调用。

nelmio_cors:
   defaults:
       allow_credentials: false
       allow_origin: []
       allow_headers: []
       allow_methods: []
       expose_headers: []
       max_age: 0
       hosts: []
       origin_regex: false
   paths:
       '^/api/':
           allow_credentials: true
           allow_origin: ['*']
           allow_headers: ['*']
           allow_methods: ['POST', 'PUT', 'GET', 'DELETE', 'OPTIONS']
           max_age: 0

Security.yml配置

providers:
    braune_digital_api_base:
            id: braune_digital_api_base.security.apikey_user_provider
    firewalls:
        api_doc: #Open API Documentation
            pattern: ^/api/doc
            anonymous: true
            security: false
        api_login:
            pattern: ^/api/v1/login$
            anonymous: true
        api_password_reset:
            pattern: ^/api/v1/password-((request$)|(reset$))
            anonymous: true
        api: #Secured API-Area
            pattern: ^/api
            stateless: true #we are using tokens
            simple_preauth:
                authenticator: braune_digital_api_base.security.apikey_authenticator #use apikeys for authentication
            provider: braune_digital_api_base #use apikeys for authentication

使用

BaseApiController

BaseApiController提供底层逻辑以快速轻松地创建API端点:只需扩展BrauneDigital\ApiBaseBundle\Controller\BaseApiController并添加你的函数

<?php

namespace BrauneDigital\DemoBundle\Controller\V1;

use BrauneDigital\ApiBaseBundle\Controller\BaseApiController;
use BrauneDigital\DemoBundle\Form\Type\ProjectType;
use BrauneDigital\Pitcher\BaseBundle\Entity\Company;
use BrauneDigital\Pitcher\BaseBundle\Entity\Project;
use Doctrine\Common\Collections\ArrayCollection;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use FOS\RestBundle\Controller\Annotations as Rest;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\HttpFoundation\Request;

/**
 * @author    Patrick Rathje <pr@braune-digital.com>
 * @copyright 2016 Braune Digital GmbH
 */
class ProjectController extends BaseApiController {

    protected function getRepository() {
        return $this->getDoctrine()->getRepository('BrauneDigitalDemoBundle:Project');
    }

    /**
     *
     * @ApiDoc(
     *  resource=false,
     *  section="Project",
     *  description="Get a project by id",
     *  requirements= {
     *      {"name": "id", "description":"Project-ID", "dataType": "integer"},
     *      {"name": "_format", "description":"Response-Format", "requirement": "json|xml|html", "dataType": "string"},
     *     {"name": "version", "description":"API-Version", "requirement": "json|xml|html", "dataType": "integer"}
     * }
     *)
     *
     * @param Request $request
     * @param $id
     * @return \FOS\RestBundle\View\View
     *
     * @Rest\Get("/projects/{id}", name="project_read", defaults={"_format": "json"})
     */
    public function readAction(Request $request, $id) {
        return parent::readAction($request, $id);
    }

    /**
     *
     * @ApiDoc(
     *  resource=false,
     *  section="Project",
     *  description="Get projects",
     *  requirements= {
     *      {"name": "_format", "description":"Response-Format", "requirement": "json|xml|html", "dataType": "string"},
     *     {"name": "version", "description":"API-Version", "requirement": "json|xml|html", "dataType": "integer"}
     *  }
     *)
     *
     * @param Request $request
     * @param $id
     * @return \FOS\RestBundle\View\View
     *
     * @Rest\Get("/projects", name="project_list", defaults={"_format": "json"})
     */
    public function listAction(Request $request) {
        return parent::listAction($request);
    }


    /**
     *
     * @ApiDoc(
     *  resource=false,
     *  section="Project",
     *  description="Create a project",
     * requirements= {
     *      {"name": "_format", "description":"Response-Format", "requirement": "json|xml|html", "dataType": "string"},
     *     {"name": "version", "description":"API-Version", "requirement": "json|xml|html", "dataType": "integer"}
     * },
     *  input="BrauneDigital\DemoBundle\Form\Type\ProjectType"
     *)
     *
     * @param Request $request
     * @param $id
     * @return \FOS\RestBundle\View\View
     *
     * @Rest\Post("/projects", name="project_create", defaults={"_format": "json"})
     */
    public function createAction(Request $request, $entity = null, $refresh = false, $formOptions = null) {
        $entity = new Project();
        return parent::createAction($request, $entity);
    }

    /**
     *
     * @ApiDoc(
     *  resource=false,
     *  section="Project",
     *  description="Update a project",
     * requirements= {
     *     {"name": "id", "description":"Project-ID", "dataType": "integer"},
     *      {"name": "_format", "description":"Response-Format", "requirement": "json|xml|html", "dataType": "string"},
     *     {"name": "version", "description":"API-Version", "requirement": "json|xml|html", "dataType": "integer"}
     * },
     *  input="BrauneDigital\DemoBundle\Form\Type\ProjectType"
     *)
     *
     * @param Request $request
     * @param $id
     * @return \FOS\RestBundle\View\View
     *
     * @Rest\Post("/projects/{id}", name="project_update", defaults={"_format": "json"})
     */
    public function updateAction(Request $request, $id, $refresh = false, $formOptions = null) {
        //add validation groups
        return parent::updateAction($request, $id, false, array('validation_groups' => array('ProjectUpdate')));
    }

    /**
     *
     * @ApiDoc(
     *  resource=false,
     *  section="Project",
     *  description="Delete a project",
     * requirements= {
     *     {"name": "id", "description":"Project-ID", "dataType": "integer"},
     *      {"name": "_format", "description":"Response-Format", "requirement": "json|xml|html", "dataType": "string"},
     *     {"name": "version", "description":"API-Version", "requirement": "json|xml|html", "dataType": "integer"}
     * }
     *)
     *
     * @param Request $request
     * @param $id
     * @return \FOS\RestBundle\View\View
     *
     * @Rest\Delete("/projects/{id}", name="project_delete", defaults={"_format": "json"})
     */
    public function deleteAction(Request $request, $id) {
        return parent::deleteAction($request, $id);
    }

    /**
    * Override the getForm Method for create and update functionalities, you may want to return different forms accordings to the mode
    **/
    protected function getForm($entity, $mode = '', $options = array()) {
        return $this->createForm(new ProjectType(), $entity, $options);
    }
}

你必须指定一个仓库,如果你想要创建或更新实体,你可能需要重写getForm函数。

安全系统

为了限制对单个资源的访问,你需要使用symfony投票者。查看BrauneDigital\ApiBaseBundle\Security\Authorization\Voter\BaseCrudVoter,它指定了用于对应路由的属性。

过滤列表操作

要过滤列表操作,可以重写createListQueryBuilder($alias = 'e')方法。可以在返回之前自定义querybuilder。

序列化组(需要JMSSerializerBundle)

序列化组由JMS Serializer用于更好地控制序列化过程。

在你的控制器中

你可以轻松使用$this->addSerializationGroup($group)添加序列化组,或者通过调用$this->serializationGroups($groups)设置它们。

使用API请求头

客户端还可以通过在请求中设置serializationGroups头来自定义序列化组。该头可以是简单的字符串,逗号分隔的字符串或字符串数组。

ApiKey认证

为了使用API令牌,你必须向你的User-Class添加令牌

    protected $token;
    
    /**
     * @return mixed
     */
    public function getToken()
    {
        return $this->token;
    }

    /**
     * @param mixed $token
     */
    public function setToken($token)
    {
        $this->token = $token;
    }

并添加你的DB映射(例如,DoctrineORM)

fields:
    token:
        type: string
        nullable: true

模块访问

此Bundle提供了一个模块访问注解,可以用于限制特定路由的访问权限。与Symfony投票系统相比,这基于API端点而不是资源。导入注解

use BrauneDigital\ApiBaseBundle\Annotation\ModuleAccess;

添加你的模块

@ModuleAccess({"products", "sales"})

或者如果你只使用一个模块

@ModuleAccess("products")

如果用户有权访问其中一个模块,则将授予访问权限。在配置中定义模块

braune_digital_api_base:
  modules:
    products:
        roles: ['ROLE_ADMIN', 'ROLE_CLIENT']
    sales:
        roles: ['ROLE_SALESMAN']

示例

    /**
     * @ApiDoc(
     *  resource=false,
     *  section="Your Section",
     *  description="A nice description",
     *  requirements= {
     *      {"name": "_format", "description":"Response-Format", "requirement": "json|xml|html", "dataType": "string"}
     * }
     *)
     * @Rest\Get("/products")
     * @ModuleAccess({"products", "sales"})
     * @param Request $request
     * @return mixed
     */
    public function listAction(Request $request) {
        return parent::listAction($request);
    }

变量配置

你可以在请求中设置_braune_digital_api_base_config属性以将你的自定义配置(braune_digital_api_base.configuration)添加到响应中

//send configuration
$request->attributes->set('_braune_digital_api_base_config', true);

该配置将在响应中的configuration键下可用。

建议

API文档

我们建议使用NelmioApiDocBundle来生成干净且易于使用的API文档。

JMSSerializerBundle

待办事项

  • 在初始化服务之前检查FOSUserBundle