Aller au contenu principal
Version: Next

Appel des fonctions de classe

You can call data model class functions defined for the ORDA Data Model and singleton class functions through REST requests, so that you can benefit from the exposed API of the targeted 4D application.

Functions can be called in two ways:

  • using POST requests, with data parameters passed in the body of the request.
  • using GET requests, with parameters directly passed in the URL.

POST requests provide a better security level because they avoid running sensitive code through an action as simple as clicking on a link. However, GET requests can be more compliant with user experience, allowing to call functions by entering an URL in a browser (note: the developer must ensure no sensitive action is done in such functions).

Appeler des fonctions

The following ORDA and singleton functions can be called in REST:

Fonction de classeSyntaxe
datastore class/rest/$catalog/DataStoreClassFunction
dataclass class/rest/\{dataClass\}/DataClassClassFunction
entitySelection class/rest/\{dataClass\}/EntitySelectionClassFunction
/rest/{dataClass}/EntitySelectionClassFunction/$entityset/entitySetNumber
/rest/{dataClass}/EntitySelectionClassFunction/$filter
/rest/{dataClass}/EntitySelectionClassFunction/$orderby
entity class/rest/{dataClass}(key)/EntityClassFunction/
Classe singleton/rest/$singleton/SingletonClass/SingletonClassFunction (voir page $singleton)
note

/rest/{dataClass}/Function can be used to call either a dataclass or an entity selection function (/rest/{dataClass} returns all entities of the DataClass as an entity selection). La fonction est d'abord recherchée dans la classe Entity selection. Si elle n'est pas trouvée, elle est recherchée dans la dataclass. En d'autres termes, si une fonction portant le même nom est définie à la fois dans la classe DataClass et la classe EntitySelection, la fonction de classe de dataclass ne sera jamais exécutée.

Functions are simply called on the appropriate ORDA interface or singleton class, without (). Parameters are passed either in the body of the POST request (POST calls) or in the params collection in the URL (GET calls).

Par exemple, si vous avez défini une fonction getCity() dans la dataclass City, vous pouvez l'appeler à l'aide de la requête suivante :

POST request

/rest/City/getCity

avec des données contenues dans le body de la requête POST : ["Aguada"]

GET request

/rest/City/getCity?$params='["Aguada"]'

note

The getCity() function must have been declared with the onHttpGet keyword (see Function configuration below).

En langage 4D, cet appel est équivalent à :

$city:=ds.City.getCity("Aguada")

Function configuration

exposed

All functions allowed to be called directly from HTTP REST requests (POST or GET) must be declared with the exposed keyword. Par exemple :

exposed Function getSomeInfo() : 4D.OutgoingMessage

See Exposed vs non-exposed functions section.

onHttpGet

Functions allowed to be called from HTTP GET requests must also be specifically declared with the onHttpGet keyword. Par exemple :

//allowing GET requests
exposed onHttpGet Function getSomeInfo() : 4D.OutgoingMessage

Thread-safe

La totalité du code 4D appelé à partir de requêtes REST doit être thread-safe si le projet fonctionne en mode compilé, car le serveur REST utilise toujours des process préemptifs dans ce cas (la valeur du paramètre Utiliser un process préemptif est ignorée par le serveur REST).

info

You can restrict calls to specific ORDA functions by configuring appropriate privileges in the roles.json file.

Paramètres

You can send parameters to functions defined in ORDA user classes or singletons. Côté serveur, ils seront reçus dans les paramètres déclarés des fonctions de classe.

Les règles suivantes s'appliquent :

  • In functions called through POST requests, parameters must be passed in the body of the POST request.
  • In functions called through GET requests, parameters must be passed in the URL with "?$params=" syntax.
  • Parameters must be enclosed within a collection (JSON format).
  • Tous les types de données scalaires pris en charge dans les collections JSON peuvent être passés en tant que paramètres.
  • L'entity selection et l'entité peuvent être passées en tant que paramètres. The parameter list must contain specific attributes used by the REST server to assign data to the corresponding ORDA objects: __DATACLASS, __ENTITY, __ENTITIES, __DATASET.

See this example and this example.

Paramètre de valeur scalaire

Scalar value parameter(s) must simply be enclosed in a collection. Tous les types de données JSON sont pris en charge dans les paramètres, y compris les pointeurs JSON. Les dates peuvent être passées sous forme de chaînes au format de date ISO 8601 (par exemple, "2020-08-22T22:00:000Z").

For example, with a dataclass function getCities() receiving text parameters:

POST request

/rest/City/getCities

Paramètres dans le body : ["Aguada","Paris"]

GET request

/rest/City/getCities?$params='["Aguada","Paris"]'

Paramètre d'entité

Les entités passées en paramètres sont référencées sur le serveur via leur clé (c'est-à-dire la propriété __KEY). If the key parameter is omitted in a request, a new entity is loaded in memory on the server. Vous pouvez également transmettre des valeurs pour tous les attributs de l'entité. Ces valeurs seront automatiquement utilisées pour l'entité traitée sur le serveur.

Si la requête envoie des valeurs d'attribut modifiées pour une entité existante sur le serveur, la fonction de modèle de données ORDA appelée sera automatiquement exécutée sur le serveur avec des valeurs modifiées. Cette fonctionnalité vous permet, par exemple, de vérifier le résultat d'une opération sur une entité, après avoir appliqué toutes les règles métier, depuis l'application cliente. Vous pouvez alors décider de sauvegarder ou non l'entité sur le serveur.

PropriétésTypeDescription
Attributs de l'entitémixteOptionnelle - Valeurs à modifier
__DATACLASSTextObligatoire - Indique la Dataclass de l'entité
__ENTITYBooleanObligatoire - Vrai pour indiquer au serveur que le paramètre est une entité
__KEYmixte (type identique à celui de la clé primaire)Optionnel - clé primaire de l'entité
  • If __KEY is not provided, a new entity is created on the server with the given attributes.
  • If __KEY is provided, the entity corresponding to __KEY is loaded on the server with the given attributes

See examples for creating or updating entities with POST requests. See an example of contents downloading using an entity with a GET request.

Paramètre d'entité associé

Mêmes propriétés que pour un paramètre d'entité. De plus, l'entité associée doit exister et est référencée par __KEY, qui contient sa clé primaire.

Reportez-vous aux exemples de création ou de mise à jour des entités avec des entités associées.

Paramètre d'entity selection

L'entity selection doit avoir été définie au préalable à l'aide de $method=entityset.

Si la requête envoie une entity selection modifiée au serveur, la fonction de modèle de données ORDA appelée sera automatiquement exécutée sur le serveur avec l'entity selection modifiée.

PropriétésTypeDescription
Attributs de l'entitémixteOptionnelle - Valeurs à modifier
__DATASETTextObligatoire - entitySetID (UUID) de la sélection d'entité
__ENTITIESBooleanObligatoire - Vrai pour indiquer au serveur que le paramètre est une sélection d'entité

See example for receiving an entity selection with a POST request. See example for getting a list built upon an entity selection with a GET request.

POST request examples

Cette base de données est exposée comme un datastore distant sur localhost (port 8111) :

alt-text

Utiliser une fonction de classe de datastore

La classe de DataStore US_Cities fournit une API :

// DataStore class

Class extends DataStoreImplementation

exposed Function getName() : Text
return "US cities and zip codes manager"

Vous pouvez lancer cette requête :

POST 127.0.0.1:8111/rest/$catalog/getName

Résultat

{
"result": "US cities and zip codes manager"
}

Utiliser une fonction de classe de dataclass

La classe de Dataclass City fournit une API qui retourne une entité de ville à partir du nom passé en paramètre :

// City class

Class extends DataClass

exposed Function getCity($city : Text ) : cs.CityEntity
return This.query("name = :1";$city).first()

Vous pouvez lancer cette requête :

POST 127.0.0.1:8111/rest/City/getCity

Requête : ["Paris"]

Résultat

Le résultat est une entité :

{
"__entityModel": "City",
"__DATACLASS": "City",
"__KEY": "1",
"__TIMESTAMP": "2020-03-09T08:03:19.923Z",
"__STAMP": 1,
"ID": 1,
"name": "Aguada",
"countyFIPS": 72003,
"county": {
"__deferred": {
"uri": "/rest/County(72003)",
"__KEY": "72003"
}
},
"zips": {
"__deferred": {
"uri": "/rest/City(1)/zips?$expand=zips"
}
}
}

Utiliser une fonction de classe d'entité

La classe d'entité CityEntity fournit une API :

// CityEntity class

Class extends Entity

exposed Function getPopulation()
return This.zips.sum("population")

Vous pouvez lancer cette requête :

POST 127.0.0.1:8111/rest/City(2)/getPopulation

Résultat

{
"result": 48814
}

Utiliser une fonction de classe d'entity selection

La classe d'entity selection CityEntity fournit une API :

// CitySelection class

Class extends EntitySelection

exposed Function getPopulation()
return This.zips.sum("population")

Vous pouvez lancer cette requête :

POST 127.0.0.1:8111/rest/City/getPopulation/?$filter="ID<3"

Résultat

{
"result": 87256
}

Utiliser une fonction de classe d'entity selection et un entity set

La classe StudentsSelection a une fonction getAgeAverage :

// StudentsSelection Class

Class extends EntitySelection

exposed Function getAgeAverage : Integer
var $sum : Integer
var $s : Object

$sum:=0
For each ($s;This)
$sum:=$sum+$s.age()
End for each
return $sum/This.length

Une fois que vous avez créé un ensemble d'entité, vous pouvez lancer cette requête :

POST 127.0.0.1:8044/rest/Students/getAgeAverage/$entityset/17E83633FFB54ECDBF947E5C620BB532

Résultat

{
"result": 34
}

Utiliser une fonction de classe d'entity selection et un "orderBy"

La classe StudentsSelection a une fonction getLastSummary :

// StudentsSelection Class


Class extends EntitySelection

exposed Function getLastSummary : Text
var $last : Object

$last:=This.last()
return =$last.firstname+" - "+$last.lastname+" is ... "+String($last.age())

Vous pouvez lancer cette requête :

POST 127.0.0.1:8044/rest/Students/getLastSummary/$entityset/?$filter="lastname=b@"&$orderby="lastname"

Résultat

{
"result": "Wilbert - Bull is ... 21"
}

Utiliser une entité à créer sur le serveur

La classe de Dataclass Students possède la fonction pushData() qui reçoit une entité contenant les données du client. La méthode checkData() effectue quelques contrôles. Si elles sont valides, l'entité est sauvegardée et retournée.

// Students Class

Class extends DataClass

exposed Function pushData($entity : Object) : Object
var $status : Object

$status:=checkData($entity) // $status est un objet avec une propriété booléenne success

If ($status.success)
$status:=$entity.save()
If ($status.success)
return $entity
End if
End if

return $status

Lancez cette requête :

POST http://127.0.0.1:8044/rest/Students/pushData

Body de la requête :

[{
"__DATACLASS":"Students",
"__ENTITY":true,
"firstname":"Ann",
"lastname":"Brown"
}]

Si aucune __KEY n'est donnée, une nouvelle entité Students est chargée sur le serveur avec les attributs du client. Parce que la fonction pushData() exécute une action save(), la nouvelle entité est créée.

Résultat

{
"__entityModel": "Students",
"__DATACLASS": "Students",
"__KEY": "55",
"__TIMESTAMP": "2020-06-16T10:54:41.805Z",
"__STAMP": 1,
"ID": 55,
"firstname": "Ann",
"lastname": "BROWN",
"schoolID": null,
"school": null
}

Utiliser une entité à mettre à jour sur le serveur

Description semblable à la précédente, avec l'attribut _KEY

Lancez cette requête :

POST:http://127.0.0.1:8044/rest/Students/pushData

Body de la requête :

[{
"__DATACLASS":"Students",
"__ENTITY":true,
"lastname":"Brownie",
"__KEY":55
}]

Si aucune __KEY n'est donnée, l'entité Students est chargée avec la clé primaire 55 avec la valeur lastname reçue par le client. Parce que la fonction exécute une action save(), la nouvelle entité est mise à jour.

Résultat

{
"__entityModel": "Students",
"__DATACLASS": "Students",
"__KEY": "55",
"__TIMESTAMP": "2020-06-16T11:10:21.679Z",
"__STAMP": 3,
"ID": 55,
"firstname": "Ann",
"lastname": "BROWNIE",
"schoolID": null,
"school": null
}

Créer une entité avec une entité liée

Dans cet exemple, nous créons une nouvelle entité Students avec l'entité Schools ayant la clé primaire 2.

Lancez cette requête :

POST:http://127.0.0.1:8044/rest/Students/pushData

Body de la requête :

[{
"__DATACLASS":"Students",
"__ENTITY":true,
"firstname":"John",
"lastname":"Smith",
"school":{"__KEY":2}
}]

Résultat

{
"__entityModel": "Students",
"__DATACLASS": "Students",
"__KEY": "56",
"__TIMESTAMP": "2020-06-16T11:16:47.601Z",
"__STAMP": 1,
"ID": 56,
"firstname": "John",
"lastname": "SMITH",
"schoolID": 2,
"school": {
"__deferred": {
"uri": "/rest/Schools(2)",
"__KEY": "2"
}
}
}

Mettre à jour une entité avec une entité liée

Dans cet exemple, nous associons une école existante à l'entité Students. La classe StudentsEntity possède une API :

// StudentsEntity class

Class extends Entity

exposed Function putToSchool()
var $1, $school , $0, $status : Object

//$1 est une entité school
$school:=$1
//associer l'entité liée school à l'entité courante students
This.school:=$school

$status:=This.save()

$0:=$status

Vous exécutez cette requête, appelée sur une entité Students : POST http://127.0.0.1:8044/rest/Students(1)/putToSchool Body de la requête :

[{
"__DATACLASS":"Schools",
"__ENTITY":true,
"__KEY":2
}]

Résultat

{
"result": {
"success": true
}
}

Recevoir une entity selection comme paramètre

Dans la classe de Dataclass Students, la fonction setFinalExam() met à jour une sélection d'entité reçue ($1). Elle met à jour l'attribut finalExam avec la valeur reçue ($2). Elle retourne les clés primaires des entités mises à jour.

// Students class

Class extends DataClass

exposed Function setFinalExam()


var $1, $es, $student, $status : Object
var $2, $examResult : Text

var $keys, $0 : Collection

//Entity selection
$es:=$1

$examResult:=$2

$keys:=New collection()

//Loop on the entity selection
For each ($student;$es)
$student.finalExam:=$examResult
$status:=$student.save()
If ($status.success)
$keys.push($student.ID)
End if
End for each

$0:=$keys

Un ensemble d'entité est d'abord créé avec cette requête :

http://127.0.0.1:8044/rest/Students/?$filter="ID<3"&$method=entityset

Vous pouvez ensuite exécuter cette requête :

POST http://127.0.0.1:8044/rest/Students/setFinalExam

Body de la requête :

[
{
"__ENTITIES":true,
"__DATASET":"9B9C053A111E4A288E9C1E48965FE671"
},
"Passed"
]

Résultat

Les entités ayant les clés primaires sont 1 et 2 ont été mises à jour.

{
"result": [
1,
2
]
}

Utiliser une entity selection mise à jour sur le client

A l'aide de la fonction getAgeAverage() définie ci-dessus.

var $remoteDS, $newStudent, $students : Object
var $ageAverage : Integer

$remoteDS:=Open datastore(New object("hostname" ; "127.0.0.1:8044") ; "students")

// $newStudent est une entité étudiant à traiter
$newStudent:=...
$students:=$remoteDS.Students.query("school.name = :1" ; "Math school")
// Nous ajoutons une entité à l'entity selection $students sur le client
$students.add($newStudent)

// Nous appelons une fonction de la classe StudentsSelection qui renvoie la moyenne d'âge des étudiants de l'entity selection
// La fonction est exécutée sur le serveur dans l'entity selection $students mise à jour qui comprend l'étudiant ajouté par le client
$ageAverage:=$students.getAgeAverage()

GET request examples

Returning a document

You want to propose a link to download the user manual for a selected product with several formats available. You write a getUserManual() function of the Products dataclass. You return an object of the OutgoingMessage class.

// Product dataclass
exposed onHTTPGet Function getUserManual($productId : Integer; $type : Text) : 4D.OutgoingMessage

var $file : 4D.File
var $response:=4D.OutgoingMessage.new()
var $doc:="/RESOURCES/User manuals/product_"+String($productId)

Case of
: ($type="pdf")
$file:=File($doc+".pdf")
$response.setBody($file.getContent()) // This is binary content
$response.setHeader("Content-Type"; "application/pdf")

: ($type="jpeg")
$file:=File($doc+".jpeg")
$response.setBody($file.getContent()) // This is binary content
$response.setHeader("Content-Type"; "image/jpeg")
End case

return $response

You can call the function using a request like:

GET http://127.0.0.1:8044/rest/Products/getUserManual?$params='[1,"pdf"]'

Using an entity to download a PDF document

Same example as above but you want to pass an entity as parameter to the datastore function.

// Product dataclass
exposed onHTTPGet Function getUserManual($product : cs.ProductEntity) : 4D.OutgoingMessage

var $file : 4D.File
var $response := 4D.OutgoingMessage.new()

$file:=File("/RESOURCES/User manuals/"+$product.name+".pdf")
$response.setBody($file.getContent())
$response.setHeader("Content-Type"; "application/pdf")

return $response

You can call the function using this request:

GET http://127.0.0.1:8044/rest/Product/getUserManual?$params='[{"__DATACLASS":"Product","__ENTITY":true,"__KEY":41}]'

Using an entity selection to get a list

You want to send an entity selection as parameter to a singleton function using a REST GET request and return a list using an object of the OutgoingMessage class.

shared singleton Class constructor()

exposed onHTTPGet Function buildShoppingList($products : cs.ProductSelection) : 4D.OutgoingMessage

var $p : cs.ProductsEntity
var $content : Text
var $response := 4D.OutgoingMessage.new()

$content:=""

For each ($p; $products)
$content:=$content+" "+$p.manufacturer+" - "+$p.name
$content:=$content+Char(Carriage return)
End for each

$response.setBody($content)
$response.setHeader("Content-Type"; "text/plain")

return $response

You can call the function using this request:

GET http://127.0.0.1:8044/rest/$singleton/Shopping/buildShoppingList?$params='[{"__DATASET":"8DB0556854HDK52FR5974F","__ENTITIES":true}]'