2014-06-16 12 views
10

Ich habe eine Flaschen-Anwendung mit Anrufen, die JSON-Nutzlast erwarten. Vor jedem Anruf verarbeitet wird, habe ich einen 2-stufige Fehlerprüfung Prozess:Flask: Decorator zum Überprüfen von JSON und JSON Schema

  • Assert, dass die Nutzlast ein gültiger JSON
  • Assert ist, dass die JSON Nutzlast mit einem bestimmten Schema

erfüllt Welche umgesetzt wird in der folgenden Art und Weise: ist

@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 

Da dieser Code über viele Anrufe dupliziert, frage ich mich, wenn ich elegant es zu einem Dekorateur bewegen kann, etwas in der formof:

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

Das Problem ist, dass das request Argument ist implizit: ich es innerhalb der Funktion beziehen kann, aber es ist kein Parameter zu. Daher bin ich mir nicht sicher, wie ich es im Dekorateur verwenden soll.

Wie kann ich die Validierungsprüfungen mithilfe von Python-Dekoratoren implementieren?

+0

Ich hatte gehofft, dass es eine Bibliothek dafür geben würde. @ Martijns Antwort ist ziemlich einfach, aber weiß jemand von einem? –

Antwort

24

Verwenden Sie einfach den request Kontext global in Ihrem Dekorateur. Es ist verfügbar während jeder Anfrage.

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 

gelten diese Dekorateure vor die @route Dekorateur Anwendung; Sie möchten die verpackte Funktion und nicht die ursprüngliche Funktion für die Route registrieren:

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

Ist die Reihenfolge der Dekorateure wichtig? Sollte der '@ app.route' Dekorateur an erster Stelle stehen? –

+1

@AdamMatan: '@route()' registriert die aufrufbar, es sollte oben auf die dekorierte Ansicht Funktion angewendet werden. Ich werde das ebenso hinzufügen wie die Implementierungen. –

+0

Sollte '@ validate_json' nicht ganz unten in der Decorator-Liste stehen? –