Skip to content
Snippets Groups Projects
REST.rst 16.3 KiB
Newer Older
.. ==================================================
.. ==================================================
.. ==================================================
.. 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 <http://getbootstrap.com/>`_
.. Internal Link: :ref:`downloadButton` (default url text) or :ref:`download Button<downloadButton>` (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 <https://en.wikipedia.org/wiki/Representational_state_transfer>`_ 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: ``<domain>/typo3conf/ext/qfq/Classes/Api/rest.php``

    ``<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/<level1>/<id1>/<level2>/<id2>/.../?<var1>=<value1>&...``


Append level names and ids after ``.../rest.php/``, each separated by '/' .
1. List of all persons: ``<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/person``
2. Data of person 123: ``<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/person/123``
3. Addresses of person 123: ``<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/person/123/address``
4. Address details of address 45 from person 123: ``<domain>/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 <level> 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
    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              | *<level>* 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.                         |
Carsten  Rose's avatar
Carsten Rose committed
|                   | | ``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              | *<level>* 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=<column>`` is necessary.
* A regular QFQ form can be used as REST Post endpoint.

PUT - Update
------------

**Form**

+-------------------+------------------------------------------------------------------------------+
| Attribute         | Description                                                                  |
+===================+==============================================================================+
| name              | *<level>* 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=<column>`` is necessary.
* A regular QFQ form can be used as REST Post endpoint

DELETE - Delete
---------------

**Form**

+-------------------+------------------------------------------------------------------------------+
| Attribute         | Description                                                                  |
+===================+==============================================================================+
| name              | *<level>* 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.

Carsten  Rose's avatar
Carsten Rose committed
Restrict access via:
Carsten  Rose's avatar
Carsten Rose committed
* 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=<secret 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).