Routing
FoxServer verwendet HELP-Kommentare in VFP-Methoden zur Routendeklaration. Es gibt keine separaten Routenkonfigurationsdateien: die Deklaration befindet sich direkt neben dem Controller-Code.
HELP-Kommentar-Syntax
Jede Endpunkt-Methode muss einen HELP-Kommentar in diesem Format haben:
PROCEDURE NombreMetodo(req, res) HELP "METODO: ruta/path [public]" | Teil | Beschreibung | Beispiel |
|---|---|---|
| HTTP-Methode | GET, POST, PUT, PATCH, DELETE, HEAD | GET |
| Route | Pfad relativ zum Server-Prafix. Kann {Parameter} enthalten. | products/{id} |
| Sichtbarkeit | public = kein JWT erforderlich. Ohne dieses Token = JWT obligatorisch. | public |
Unterstutzte HTTP-Methoden
| Methode | Beschreibung | Hat Body? | Routenbeispiel |
|---|---|---|---|
GET | Daten abrufen | Nein | GET: products public |
POST | Ressource erstellen | Ja | POST: products public |
PUT | Aktualisieren (vollstandig) | Ja | PUT: products/{id} public |
PATCH | Aktualisieren (partiell) | Ja | PATCH: products/{id} public |
DELETE | Ressource loschen | Nein | DELETE: products/{id} public |
HEAD | Nur Header | Nein | HEAD: products public |
URL-Parameter
* Single parameter
PROCEDURE GetProduct(req, res) HELP "GET: products/{id} public"
LOCAL lcId
lcId = req.params.id && /api/products/42 -> "42"
res.status(200).json(...)
ENDPROC
* Multiple parameters
PROCEDURE GetVariant(req, res) HELP "GET: products/{id}/variants/{vid} public"
LOCAL lcProductId, lcVariantId
lcProductId = req.params.id && /api/products/42/variants/black -> "42"
lcVariantId = req.params.vid && "black"
res.status(200).json(...)
ENDPROC Query-String
Query-String-Parameter (?Schlussel=Wert) werden uber req.query abgerufen:
PROCEDURE SearchProducts(req, res) HELP "GET: products/search public"
* GET /api/products/search?name=Widget&minprice=10&page=2
LOCAL lcName, lnMin, lnPage
lcName = req.query.name && "Widget"
lnMin = VAL(req.query.minprice) && 10
lnPage = IIF(EMPTY(req.query.page), 1, VAL(req.query.page))
res.status(200).json(...)
ENDPROC Request-Objekt — Eigenschaften
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
method | String | HTTP-Methode (GET, POST, usw.) |
url | Uri | Vollstandige Anfrage-URL |
path | String | Pfad ohne Query-String |
params | Object | URL-Parameter ({id} → params.id) |
query | Object | Query-String-Parameter (?key=val) |
headers | Dict | HTTP-Header (Gro-/Kleinschreibung egal) |
body | String | Anfrage-Body als Klartext |
json | Object | Geparster JSON-Body (wenn Content-Type: application/json) |
contentType | String | Wert des Content-Type-Headers |
remoteIP | String | IP-Adresse des Clients |
Response-Objekt — Methoden
| Methode | Signatur | Beschreibung |
|---|---|---|
status(code) | res.status(200) | Setzt den HTTP-Code. Gibt res fur Verkettung zuruck. |
json(jsonStr) | res.json('{"ok":true}') | Sendet JSON-Body. Setzt Content-Type: application/json. |
send(text) | res.send("Hallo") | Sendet Klartext oder HTML. |
header(key, val) | res.header("X-Id","123") | Fugt einen Antwort-Header hinzu oder uberschreibt ihn. |
location(url) | res.location("/api/v2") | Setzt den Location-Header (fur Weiterleitungen). |
sendFile(path, disp) | res.sendFile("C:\\rep.pdf","attachment") | Sendet eine Datei. disp: "inline" oder "attachment". |
* Chaining example — multiple headers + JSON response
res.status(201)
.header("X-Resource-Id", lcNewId)
.header("Cache-Control", "no-cache")
.json(THIS.ToJson(loResponse)) Controller-Lebenszyklus
Jeder Controller kann Hooks definieren, die vor und nach jedem Endpunkt ausgefuhrt werden:
DEFINE CLASS ProductsController AS ApiController OLEPUBLIC
FUNCTION BeforeEndpoint(req, res) AS BOOLEAN
SET DELETED ON
SET EXCLUSIVE OFF
USE data\products SHARED
RETURN .T. && .F. here would abort the request
ENDFUNC
PROCEDURE AfterEndpoint(req, res)
CLOSE DATABASES ALL
ENDPROC
PROCEDURE GetProducts(req, res) HELP "GET: products public"
&& Products table is already open (BeforeEndpoint)
&& It will be closed automatically (AfterEndpoint)
res.status(200).json(...)
ENDPROC
ENDDEFINE Offentliche vs. geschutzte Routen
Die Sichtbarkeit wird mit dem public-Schlusselwort im HELP-Kommentar gesteuert:
* PUBLIC endpoint — no JWT needed
PROCEDURE GetProducts(req, res) HELP "GET: products public"
res.status(200).json(...)
ENDPROC
* PRIVATE endpoint — JWT required (omit "public")
PROCEDURE DeleteProduct(req, res) HELP "DELETE: products/{id}"
&& Client must send: Authorization: Bearer eyJhb...
res.status(200).json(...)
ENDPROC Wenn JWT-Middleware aktiv ist, mussen Endpunkte ohne public den Token im Header Authorization: Bearer {token} senden.
Weiter: Middleware →