2014-06-16 3 views
10

Possiedo un'applicazione di flask con chiamate che prevedono il carico utile JSON. Prima di ogni chiamata viene elaborata, ho un errore 2-step processo di verifica:Flask: Decorator per verificare JSON e JSON Schema

  • affermare che il carico utile è un JSON valido
  • affermare che il payload JSON conforme ad una specifica schema

Quale è implementato nel modo seguente:

@app.route('/activate', methods=['POST']) 
def activate(): 
    request_id = request.__hash__() 

    # Assert that the payload is a valid JSON 
    try: 
     input = request.json 
    except BadRequest, e: 
     msg = "payload must be a valid json" 
     return jsonify({"error": msg}), 400 

    # JSON Schema Validation 
    try: 
     validate(request.json, app.config['activate_schema']) 
    except ValidationError, e: 
     return jsonify({"error": e.message}), 400 

Dal momento che questo codice è duplicato su molte chiamate, mi chiedo se posso elegantemente spostarlo in un decoratore, qualcosa nel formof:

@validate_json 
@validate_schema(schema=app.config['activate_schema']) 
@app.route('/activate', methods=['POST']) 
def activate(): 
    .... 

Il problema è che l'argomento request è implicito: posso fare riferimento ad esso all'interno della funzione, ma non è un parametro ad esso. Pertanto, non sono sicuro di come utilizzarlo all'interno del decoratore.

Come è possibile implementare i controlli di convalida utilizzando i decoratori Python?

+0

Speravo che ci sarebbe stata una libreria per farlo. @ La risposta di Martijn è piuttosto semplice, ma qualcuno ne conosce uno? –

risposta

24

Basta usare il contesto request globale nel decoratore. È disponibile durante qualsiasi richiesta.

from functools import wraps 
from flask import (
    current_app, 
    jsonify, 
    request, 
) 


def validate_json(f): 
    @wraps(f) 
    def wrapper(*args, **kw): 
     try: 
      request.json 
     except BadRequest, e: 
      msg = "payload must be a valid json" 
      return jsonify({"error": msg}), 400 
     return f(*args, **kw) 
    return wrapper 


def validate_schema(schema_name): 
    def decorator(f): 
     @wraps(f) 
     def wrapper(*args, **kw): 
      try: 
       validate(request.json, current_app.config[schema_name]) 
      except ValidationError, e: 
       return jsonify({"error": e.message}), 400 
      return f(*args, **kw) 
     return wrapper 
    return decorator 

Applicare questi decoratori prima di applicare il@route decoratore; vuoi registrare la funzione incartata, non la funzione originale per il percorso:

@app.route('/activate', methods=['POST']) 
@validate_json 
@validate_schema('activate_schema') 
def activate(): 
    input = request.json 
+0

L'ordine dei decoratori è importante? Il decoratore '@ app.route' dovrebbe venire prima? –

+1

@AdamMatan: '@route()' registra il callable, dovrebbe venire in cima per essere applicato alla funzione di visualizzazione decorata. Aggiungerò che oltre alle implementazioni. –

+0

Non dovresti '@ validate_json' andare in fondo alla lista decoratori? –