10.5. Una API de control para la cámara#
El propietario necesita ajustar la sensibilidad del detector de movimiento desde cualquier lugar: el viento mueve más los árboles en un día ventoso. Eso significa rutas desde las que el panel de control pueda leer la configuración actual y a las que pueda enviar cambios.
Un pequeño diccionario de estado compartido en el módulo basta para guardar los controles. Las páginas posteriores le añaden más claves; por ahora solo hay una:
state = {
'threshold': 12,
'frame_count': 0,
'trigger_count': 0,
}
10.5.1. GET para leer, POST para escribir#
Un par de rutas – una get y una post – da al panel de control acceso de lectura/escritura a state:
from microdot import abort
@app.get('/config')
async def get_config(request):
return state
@app.post('/config')
async def set_config(request):
body = request.json
if not body or 'threshold' not in body:
abort(400, 'missing threshold')
try:
threshold = int(body['threshold'])
except (TypeError, ValueError):
abort(400, 'threshold must be an integer')
if not 0 <= threshold <= 100:
abort(400, 'threshold out of range')
state['threshold'] = threshold
return {'ok': True, 'threshold': threshold}
microdot.Request.json devuelve el cuerpo analizado como JSON, o None si Content-Type no era application/json. El manejador post recorre cada modo de fallo – clave faltante, tipo incorrecto, fuera de rango – y aborta con microdot.abort(), que lanza microdot.HTTPException para cortocircuitar el manejador con el estado y el mensaje indicados.
10.5.2. GET, POST, PUT, DELETE#
get() y post() son los dos que usaremos más. put() y delete() existen para casos que siguen las convenciones REST: un PUT /events/42 para reemplazar el evento 42, un DELETE /events/42 para eliminarlo. El manejador es por lo demás idéntico.
10.5.3. Leer cadenas de consulta y formularios#
El panel de control envía JSON, así que request.json es lo que queremos. Otras dos maneras en que la cámara podría recibir datos:
args– la cadena de consulta.?foo=1&bar=2se convierte en unmicrodot.MultiDictque puedes leer conrequest.args.get('foo').form– un formulario HTML enviado comoapplication/x-www-form-urlencoded. El mismo tipoMultiDict.
El MultiDict es similar a un diccionario, pero permite que una clave lleve varios valores (?tag=cat&tag=dog son dos valores de tag); consulta microdot.MultiDict para ver la superficie completa.
10.5.4. Segmentos de URL dinámicos#
La ruta de un endpoint puede declarar marcadores de posición con tipo que microdot pasa al manejador como argumentos adicionales:
@app.get('/events/<int:event_id>')
async def get_event(request, event_id):
return {'id': event_id, 'msg': 'placeholder'}
Los convertidores admitidos son <int:>, <re:> para una expresión regular personalizada, <path:> para un segmento que puede contener barras, y el predeterminado (sin prefijo) para «coincidir con cualquier cosa hasta la siguiente barra». <int:event_id> acepta /events/42 y rechaza /events/abc; el rechazo se convierte en un 404 sin que el manejador llegue a ejecutarse.
10.5.5. Respuestas de error personalizadas#
El 404 predeterminado que envía microdot es un simple Not found. El panel de control espera JSON en cada respuesta; sobrescribe el manejador del 404 para que también devuelva JSON:
@app.errorhandler(404)
async def not_found(request):
return {'error': 'not found', 'path': request.path}, 404
errorhandler() toma o bien un código de estado (captura todos los errores de ese estado) o bien una clase de excepción (captura todo manejador que lanzó esa excepción). La tupla (body, status) cortocircuita la respuesta sin construir un Response.
La cámara ahora expone su estado y acepta modificaciones.