-
Carsten Rose authoredCarsten Rose authored
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: <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 '/' .
E.g.:
- List of all persons:
<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/person
- Data of person 123:
<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/person/123
- Addresses of person 123:
<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/person/123/address
- 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 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 | <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. | ``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.
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=<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).