.. ================================================== .. ================================================== .. ================================================== .. Header hierarchy .. == .. -- .. ^^ .. "" .. ;; .. ,, .. .. --------------------------------------------used to the update the records specified ------ .. Best Practice T3 reST: https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/WritingReST/CheatSheet.html .. Reference: https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/WritingReST/Index.html .. Italic *italic* .. Bold **bold** .. Code ``text`` .. External Links: `Bootstrap `_ .. Internal Link: :ref:`downloadButton` (default url text) or :ref:`download Button` (explicit url text) .. Add Images: .. image:: ./Images/a4.jpg .. .. .. Admonitions .. .. note:: .. important:: .. tip:: .. warning:: .. Color: (blue) (orange) (green) (red) .. .. Definition: .. some text becomes strong (only one line) .. description has to indented .. -*- coding: utf-8 -*- with BOM. .. include:: Includes.txt .. _`restApi`: REST ==== Via `REST `_ it's possible to access the QFQ based application. Each REST API endpoint has to be defined as a QFQ Form. This describes the server side (=QFQ is server). For client access check :ref:`rest_client`. The QFQ REST api implements the four most used REST HTTP methods: GET - Read Shows a list of database records or a single record. The QFQ form holds the definition which and what to show. List: ``curl -X GET "http://localhost/qfq/typo3conf/ext/qfq/Classes/Api/rest.php/person/`` Data (id=123): ``curl -X GET "http://localhost/qfq/typo3conf/ext/qfq/Classes/Api/rest.php/person/123`` POST - Create new record The QFQ form defines wich columns will be written in which table. Most of QFQ Form functionality can be used. Example: ``curl -X POST "http://localhost/qfq/typo3conf/ext/qfq/Classes/Api/rest.php/person/" -d '{"name":"Miller","firstname":"Joe"}'`` PUT - Update a record Similar to POST, but a given record will be updated. ``curl -X PUT "http://localhost/qfq/typo3conf/ext/qfq/Classes/Api/rest.php/person/123" -d '{"name":"Miller","firstname":"Joe"}'`` DELETE - Delete a record Similar to a QFQ Delete form. ``curl -X DELETE "http://localhost/qfq/typo3conf/ext/qfq/Classes/Api/rest.php/person/123"`` All data will be imported / exported in JSON notation. Any QFQ form becomes a REST form via: ``Form > Access > Permit REST: get / insert / update / delete`` If the REST endpoint specifies an unknown form or access is forbidden, an HTTP error is reported. Endpoint -------- .. tip:: The basic REST API endpoint: ``/typo3conf/ext/qfq/Classes/Api/rest.php`` ``/typo3conf/ext/qfq/Classes/Api/rest.php/////.../?=&...`` Append level names and ids after ``.../rest.php/``, each separated by '/' . E.g.: 1. List of all persons: ``/typo3conf/ext/qfq/Classes/Api/rest.php/person`` 2. Data of person 123: ``/typo3conf/ext/qfq/Classes/Api/rest.php/person/123`` 3. Addresses of person 123: ``/typo3conf/ext/qfq/Classes/Api/rest.php/person/123/address`` 4. Address details of address 45 from person 123: ``/typo3conf/ext/qfq/Classes/Api/rest.php/person/123/address/45`` QFQ 'Forms' are used as a 'container' (to define all details). .. tip:: The QFQ ``form name`` represents the level name. Only the last of an URI will be processed. The former ones are just to fulfil a good looking REST API. .. note:: Each level name (=form name) is available via STORE_CLIENT and name ``_formX``. E.g. in example (1) ``{{_form1:C:alnumx}}=person`` and ``{{_form2:C:alnumx}}=address``. Each level id is available via STORE_CLIENT and name `_idX`. E.g. in example (2) ``{{_id1:C}}=123`` and ``{{_id2:C}}=45``. Also the ``id`` after the last ``level`` in the URI path, 123 in example (2) and 45 in example (4), is copied to variable ``r`` in STORE_TYPO3, access it via ``{{r:T}}``. GET - Read ---------- A REST (GET) form has two modes: data Specific content to a given id. Defined via ``form.parameter.restSqlData``. This mode is selected if there is an id>0 given. list A list of records will be exported. Defined via ``form.parameter.restSqlList``. This mode is selected if there is no id or id=0. .. note:: There are *no* native-FormElements necessary or loaded. Action FormElements will be processed. To simplify access to id parameter of the URI, a mapping is possible via 'form.parameter.restParam'. E.g. ``restParam=pId,adrId`` with example d) makes ``{{pId:C}}=123`` and ``{{adrId:C}}=45``. The order of variable names corresponds to the position in the URI. ``_id1`` is always mapped to the first parameter name, ``_id2`` to the second one and so on. GET Variables provided via URL are available via STORE_CLIENT as usual. **Form** +-------------------+------------------------------------------------------------------------------+ | Attribute | Description | +===================+==============================================================================+ | name | ** Mandatory. Level name (Endpoint) in URI. | +-------------------+------------------------------------------------------------------------------+ | table | Mandatory. Name of the primary table | +-------------------+------------------------------------------------------------------------------+ | Permit REST | *get* Mandatory. The form can be loaded in REST mode. | +-------------------+------------------------------------------------------------------------------+ **Form.parameter** +-------------------+----------------------------------------------------------------------------------+ | Attribute | Description | +===================+==================================================================================+ | restSqlData | Mandatory. SQL query selects content shown in data mode. | | | | ``restSqlData={{!SELECT id, name, gender FROM Person WHERE id='{{r:T0}}' }} `` | +-------------------+----------------------------------------------------------------------------------+ | restSqlList | Mandatory. SQL query selects content shown in data mode. | | | | ``restSqlData={{!SELECT id, name FROM Person }}`` | +-------------------+----------------------------------------------------------------------------------+ | restParam | Optional. CSV list of variable names. E.g.: ``restParam=pId,adrId`` | +-------------------+----------------------------------------------------------------------------------+ | restToken | Optional. User defined string or dynamic token (see :ref:`restAuthorization`). | +-------------------+----------------------------------------------------------------------------------+ .. note:: There are no :ref:`special-column-names` available in ``restSqlData`` or ``restSqlList``. Also there are no SIPs possible, cause REST typically does not offer sessions/cookies (which are necessary for SIPs). .. important:: If there is an ``ìd`` given, a record in the named primary with the specified table has to exist. If not, an error is thrown. POST - Insert ------------- **Form** +-------------------+------------------------------------------------------------------------------+ | Attribute | Description | +===================+==============================================================================+ | name | ** Mandatory. Level name (Endpoint) in URI. | +-------------------+------------------------------------------------------------------------------+ | table | Mandatory. Name of the primary table | +-------------------+------------------------------------------------------------------------------+ | Permit REST | *insert* Mandatory. The form can be loaded in REST mode. | +-------------------+------------------------------------------------------------------------------+ | id | Missing or '0'. | +-------------------+------------------------------------------------------------------------------+ **Form.parameter** +-------------------+----------------------------------------------------------------------------------+ | Attribute | Description | +===================+==================================================================================+ | restParam | Optional. CSV list of variable names. E.g.: ``restParam=pId,adrId`` | +-------------------+----------------------------------------------------------------------------------+ | restToken | Optional. User defined string or dynamic token (see :ref:`restAuthorization`). | +-------------------+----------------------------------------------------------------------------------+ | restSqlPostPut | Optional. Instead of returning the last_insert_id, a customized result might be | | | specified. E.g. ``{{! SELECT id, token FROM Token WHERE id={{id:R0}} }}`` | +-------------------+----------------------------------------------------------------------------------+ FormElement: * For each column to save one FormElement with ``FE.name=`` is necessary. * A regular QFQ form can be used as REST Post endpoint. PUT - Update ------------ **Form** +-------------------+------------------------------------------------------------------------------+ | Attribute | Description | +===================+==============================================================================+ | name | ** Mandatory. Level name (Endpoint) in URI. | +-------------------+------------------------------------------------------------------------------+ | table | Mandatory. Name of the primary table | +-------------------+------------------------------------------------------------------------------+ | Permit REST | *update* Mandatory. The form can be loaded in REST mode. | +-------------------+------------------------------------------------------------------------------+ | id | >0 | +-------------------+------------------------------------------------------------------------------+ **Form.parameter** +-------------------+----------------------------------------------------------------------------------+ | Attribute | Description | +===================+==================================================================================+ | restParam | Optional. CSV list of variable names. E.g.: ``restParam=pId,adrId`` | +-------------------+----------------------------------------------------------------------------------+ | restToken | Optional. User defined string or dynamic token (see :ref:`restAuthorization`). | +-------------------+----------------------------------------------------------------------------------+ FormElement: * For each column to save one FormElement with ``FE.name=`` is necessary. * A regular QFQ form can be used as REST Post endpoint DELETE - Delete --------------- **Form** +-------------------+------------------------------------------------------------------------------+ | Attribute | Description | +===================+==============================================================================+ | name | ** Mandatory. Level name (Endpoint) in URI. | +-------------------+------------------------------------------------------------------------------+ | table | Mandatory. Name of the primary table | +-------------------+------------------------------------------------------------------------------+ | Permit REST | *delete* Mandatory. The form can be loaded in REST mode. | +-------------------+------------------------------------------------------------------------------+ | id | >0 | +-------------------+------------------------------------------------------------------------------+ **Form.parameter** +-------------------+----------------------------------------------------------------------------------+ | Attribute | Description | +===================+==================================================================================+ | restParam | Optional. CSV list of variable names. E.g.: ``restParam=pId,adrId`` | +-------------------+----------------------------------------------------------------------------------+ | restToken | Optional. User defined string or dynamic token (see :ref:`restAuthorization`). | +-------------------+----------------------------------------------------------------------------------+ .. note:: There are *no* native-FormElements necessary - but might exist for dependent records to delete. Action FormElements will be processed. .. _`restAuthorization`: Authorization ------------- A QFQ form is only accessible via REST API, if ``Form.permitRest`` enables one of the HTTP Methods: **get, post, put, delete** ``Permit New`` or ``Permit Edit`` don't apply to QFQ forms called via REST. .. important:: By default, the REST API is public accessible. Restrict access via: * HTTP AUTH (configured via web server). * Any other web server based access restriction method. * QFQ internal 'HTTP header token based authorization' (see below). Token based authorization ^^^^^^^^^^^^^^^^^^^^^^^^^ A form will require a 'token based authorization', as soon as there is a ``form.parameter.restToken`` defined. Therefore the HTTP Header 'Authorization' has to be set with ``token=``. The 'secret token' will be checked against the server. Example: :: form.parameter.restToken=myCrypticString0123456789 Test via commandline: curl -X GET -H 'Authorization: Token token=myCrypticString0123456789' "http://localhost/qfq/typo3conf/ext/qfq/Classes/Api/rest.php/person/123/address/" The static setup with ``form.parameter.restToken=myCrypticString0123456789`` is fine, as long as only one token exist. In case of multiple tokens, replace the static string against a SQL query. .. tip:: The HTML Header Authorization token is available in STORE_CLIENT via '``{{Authorization:C:alnumx}}``. Best Practice: For example all created tokens are saved in a table 'Auth' with a column 'token'. Define:: form.parameter.restToken={{SELECT a.token FROM Auth AS a WHERE a.token='{{Authorization:C:alnumx}}' }} An invalid or empty Authorization string won't select any record in ``form.parameter.restSqlList / form.parameter.restSqlData``. To restrict access to a subset of data, just save the limitations inside the Auth record and update the query to check it:: form.parameter.restToken={{SELECT a.token FROM Auth AS a WHERE a.token='{{Authorization:C:alnumx}}'}} form.parameter.restSqlList={{!SELECT p.id, p.name, p.email FROM Person AS p, Auth AS a WHERE a.token='{{Authorization:C:alnumx}}' AND a.attribute=p.attribute}} form.parameter.restSqlData={{!SELECT p.* FROM Person AS p, Auth AS a WHERE a.token='{{Authorization:C:alnumx}}' AND a.attribute=p.attribute AND p.id='{{r:T0}}' }} If authorization is denied, the request will be answered with a delay of 3 seconds (configured via securityFailedAuthDelay).