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.