# El controlador
[← Volver al índice](../crudl.md)
`controller.dkl` es el controlador principal. Es responsable de analizar la solicitud, seleccionar el modelo y la vista apropiados para cada tipo de entidad, realizar la operación y devolver la respuesta.
## Análisis de la solicitud
### Parámetros
Los parámetros de la operación provienen de `@http_context/request/get`, es decir, de la cadena de consulta de la URL o de los marcadores resueltos por `routes.map`.
### Carga útil
La carga útil se obtiene en este orden:
1. Si `request/post` contiene campos, se usa. Corresponde a un cuerpo codificado como `application/x-www-form-urlencoded`, típico de un formulario HTML.
2. En caso contrario, se interpreta `request/post_string` como JSON.
3. Si ninguna de las dos produce datos, la carga útil es nula.
No se procesan cuerpos codificados como `multipart/form-data`; el marco no admite carga de archivos.
### Campos de control
Antes de procesar la carga útil, los campos cuyo nombre comienza con `---` se **mueven** del cuerpo de la solicitud a los parámetros. Esto permite que un formulario HTML transporte información de control junto a los datos de la entidad sin que esos campos lleguen al modelo.
```html
```
El campo anterior no formará parte de los datos que reciba el modelo, pero estará disponible como parámetro. Ver [Patrón PRG y redirección](prg-y-redireccion.md).
### Negociación de contenido
El controlador examina el encabezado `Accept` de la solicitud. Si contiene `text/html`, se activa el modo de interfaz Web; en cualquier otro caso, el modo de servicio REST.
Esta única decisión determina el formato de la respuesta, la selección de vistas y el tratamiento del método POST.
## Selección del modelo y del controlador específico
El controlador construye la ruta de la entidad concatenando `@path_root`, `@base_path` y el valor de `_entities_type`, y busca en ella:
1. **`model.dk`** — si existe, se incluye. Es el modelo específico de la entidad y puede sustituir cualquiera de los punteros de función del modelo genérico.
2. **`controller.dk`** — si existe, se incluye después del modelo. Puede sustituir el controlador de vistas o ajustar cualquier variable global antes de que se realice la operación.
Si no existe `model.dk`, se usa el modelo genérico `entity.dk`, cuyos punteros ya están establecidos.
Ver [Implementación del modelo](modelo.md).
### El controlador específico de la entidad
`controller.dk` se ejecuta después del modelo y antes de que se resuelva la operación. Es el lugar para decidir **qué** función atenderá la solicitud, mientras que `model.dk` define **cómo**.
Conviene separarlos así: el modelo declara todas las funciones disponibles sin establecer punteros, y el controlador establece los punteros según lo que se haya pedido.
```
#include "dkli.dkh"
#!
module "ControladorCliente"
{
switch @@(@http_context,"$request/get/_view")
{
case "saldo"
{
point @read to Cliente.saldo
break
}
default
{
point @list to Cliente.list
point @read to Cliente.read
point @blank to Cliente.blank
point @create to Cliente.create
point @update to Cliente.update
point @delete to Cliente.delete
}
}
}
```
Desde `controller.dk` también puede establecer `@form_view`, `@list_view`, `@url_redir` o cualquier otra variable global, con lo que una misma entidad puede presentar formularios distintos según el contexto de la solicitud.
### Subrecursos de una entidad
Una entidad de negocio suele necesitar consultas auxiliares que no son entidades por derecho propio: buscar el cliente al capturar una factura, obtener el precio de un producto, consultar los folios disponibles. Publicar cada una como entidad separada resulta artificial.
El patrón habitual es distinguirlas con un parámetro de la solicitud y reasignar el puntero de la operación de lectura o de listado:
```
GET /misistema/factura/?_view=buscar-cliente&search=lopez
```
```
case "buscar-cliente" { point @list to Factura.buscarCliente }
```
El marco no reserva ningún nombre para este propósito. `_view` es la convención adoptada en los sistemas construidos sobre CRUD-L y se recomienda por coherencia; evite emplear ese nombre para otros fines.
Recuerde que las funciones a las que apunte pueden devolver cualquier estructura, no necesariamente una lista. Ver [Contrato de las funciones](modelo.md#contrato-de-las-funciones).
## Selección de la operación
### Reescritura del método en modo HTML
Cuando `Accept` incluye `text/html` y se recibe un `POST` sobre un identificador de entidad que existe y no es el centinela de entidad nueva, el método se reescribe internamente como `PATCH`.
Esto existe porque los formularios HTML estándar solo pueden emitir `GET` y `POST`. El comportamiento de actualización se implementa así en `POST`, además de en `PUT` y `PATCH`, para facilitar la programación del lado del navegador.
### Correspondencia final
Una vez aplicada la reescritura, la operación se determina así:
| Método (tras reescritura) | `_entity_id` | Operación |
|---|---|---|
| GET | ausente | list |
| GET | presente | read |
| GET | `_new` | read con elemento en blanco |
| POST | cualquiera | create |
| PUT | cualquiera | update |
| PATCH | cualquiera | update |
| DELETE | cualquiera | delete |
El valor `_new` es configurable mediante `@entity_id_blank`. Cuando se recibe una lectura sobre ese valor, se invoca el puntero `@blank` en lugar de `@read`, lo que produce un elemento de datos vacío o con valores predeterminados. Es la forma de obtener el formulario para dar de alta.
### Uso de PUT y PATCH
Use `PUT` si va a incluir todos los campos de la entidad; en caso contrario use `PATCH`. Si usa `PUT` y no incluye todos los campos, los que se omitan podrán establecerse a nulo o a su valor predeterminado.
Ambos métodos invocan el mismo puntero `@update`. La diferencia es responsabilidad del contenido que usted envíe.
## Realización de la operación
El controlador invoca el puntero de función correspondiente:
| Operación | Puntero | Argumentos |
|---|---|---|
| create | `@create` | `&db, ¶ms, &data` |
| read | `@read` | `&db, ¶ms` |
| read con `_new` | `@blank` | `&db, ¶ms` |
| update | `@update` | `&db, ¶ms, &data` |
| delete | `@delete` | `&db, ¶ms` |
| list | `@list` | `&db, ¶ms` |
Si el puntero correspondiente es nulo, se produce un error `404` con el mensaje *Operación no admitida*. Ésta es la forma de deshabilitar operaciones concretas para una entidad: establecer el puntero a nulo en su `model.dk`.
En la operación de baja, el estado de la respuesta se establece en `204` antes de invocar el puntero.
## Selección de la vista
Terminada la operación con éxito se invoca `@success_view`; si se produjo una excepción, `@fail_view`. Ambas reciben los mismos cuatro argumentos:
```
::¶ms, &input_data, &output_data, &error_info
```
En modo REST, ambos punteros apuntan a la vista JSON genérica. En modo HTML, el controlador de vistas los reasigna según la operación. Ver [Implementación de las vistas](vistas.md).
## Tratamiento de errores
Toda la ejecución está protegida por un manejador de excepciones. Cuando se produce una:
1. Se establece el estado HTTP. Si el código del error es igual o mayor que `400`, se usa ese código; en cualquier otro caso se usa `500`.
2. Se construye un objeto de error con un solo campo:
```json
{ "message": "Texto del error" }
```
3. Se invoca `@fail_view` con ese objeto en el cuarto argumento.
En modo REST, el objeto de error se serializa como JSON y constituye el cuerpo de la respuesta. En modo HTML, se pasa a la vista de error o de formulario, según la operación.
Los códigos que produce el marco genérico son:
| Código | Situación |
|---|---|
| `204` | Baja realizada |
| `404` | Elemento no encontrado; entidad no definida; operación no admitida; identidad no indicada |
| `500` | Error de vista no disponible; cualquier error no clasificado, incluidos los errores de base de datos |
Ver [Web services REST](rest.md) para el detalle de las respuestas.
## Modelos obligatorios
La variable `@auto_crud` controla si se admite operar sobre entidades que no tienen un `model.dk` propio.
Con el valor predeterminado, una entidad sin modelo específico se atiende con el modelo genérico. Si desea que solo puedan operarse entidades con modelo explícito, establezca la variable a falso; las entidades sin `model.dk` producirán un error `404` con el mensaje *Entidad no definida*.
## Documentos relacionados
* [Implementación del modelo](modelo.md)
* [Implementación de las vistas](vistas.md)
* [Patrón PRG y redirección](prg-y-redireccion.md)
* [Conexión a la base de datos y sesión](conexion-y-sesion.md)