.. ==================================================
.. ==================================================
.. ==================================================
.. 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).