Sistema de rutas
FoxServer utiliza comentarios HELP en los metodos VFP para declarar rutas. No hay archivos de configuracion de rutas separados: la declaracion vive junto al codigo del controlador.
Sintaxis del comentario HELP
Cada metodo endpoint debe tener un comentario HELP con este formato:
PROCEDURE NombreMetodo(req, res) HELP "METODO: ruta/path [public]" | Parte | Descripcion | Ejemplo |
|---|---|---|
| Metodo HTTP | GET, POST, PUT, PATCH, DELETE, HEAD | GET |
| Ruta | Path relativo al prefijo del servidor. Puede contener {parametros}. | products/{id} |
| Visibilidad | public = sin JWT requerido. Sin este token = JWT obligatorio. | public |
Metodos HTTP soportados
| Metodo | Descripcion | Tiene body? | Ejemplo de ruta |
|---|---|---|---|
GET | Recuperar datos | No | GET: products public |
POST | Crear recurso | Si | POST: products public |
PUT | Actualizar (completo) | Si | PUT: products/{id} public |
PATCH | Actualizar (parcial) | Si | PATCH: products/{id} public |
DELETE | Eliminar recurso | No | DELETE: products/{id} public |
HEAD | Solo encabezados | No | HEAD: products public |
Parametros de URL
* 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
Los parametros de query string (?clave=valor) se acceden via req.query:
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 Objeto Request — propiedades
| Propiedad | Tipo | Descripcion |
|---|---|---|
method | String | Metodo HTTP (GET, POST, etc.) |
url | Uri | URL completa de la peticion |
path | String | Ruta sin query string |
params | Object | Parametros de URL ({id} → params.id) |
query | Object | Parametros de query string (?key=val) |
headers | Dict | Cabeceras HTTP (case-insensitive) |
body | String | Body de la peticion en texto plano |
json | Object | Body JSON parseado (si Content-Type: application/json) |
contentType | String | Valor del header Content-Type |
remoteIP | String | Direccion IP del cliente |
Objeto Response — metodos
| Metodo | Firma | Descripcion |
|---|---|---|
status(code) | res.status(200) | Establece el codigo HTTP. Retorna res para encadenamiento. |
json(jsonStr) | res.json('{"ok":true}') | Envia body JSON. Establece Content-Type: application/json. |
send(text) | res.send("Hello") | Envia texto plano o HTML. |
header(key, val) | res.header("X-Id","123") | Agrega o sobreescribe un header de respuesta. |
location(url) | res.location("/api/v2") | Establece el header Location (para redirects). |
sendFile(path, disp) | res.sendFile("C:\\rep.pdf","attachment") | Envia un archivo. disp: "inline" o "attachment". |
* Chaining example — multiple headers + JSON response
res.status(201)
.header("X-Resource-Id", lcNewId)
.header("Cache-Control", "no-cache")
.json(THIS.ToJson(loResponse)) Ciclo de vida del controlador
Cada controlador puede definir hooks que se ejecutan antes y despues de cada endpoint:
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 Rutas publicas vs. protegidas
La visibilidad se controla con la palabra clave public en el comentario HELP:
* 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 Cuando el JWT middleware esta activo, los endpoints sin public requieren que el cliente envie el token en el header Authorization: Bearer {token}.
Siguiente: Middleware →