2015-05-27 17 views
5

Moi butelek aplikacje nie były bardzo DRY, oto test-case:Walidacja DRY w butelce?

from uuid import uuid4 
from bottle import Bottle, response 

foo_app = Bottle() 

@foo_app.post('/foo') 
def create(): 
    if not request.json: 
     response.status = 400 
     return {'error': 'ValidationError', 'error_message': 'Body required'} 
    body = request.json 
    body.update({'id': uuid4().get_hex()) 
    # persist to db 
    # ORM might set 'id' on the Model layer rather than setting it here 
    # ORM will validate, as will db, so wrap this in a try/catch 
    response.status = 201 
    return body 

@foo_app.put('/foo/<id>') 
def update(id): 
    if not request.json: 
     response.status = 400 
     return {'error': 'ValidationError', 'error_message': 'Body required'} 
    elif 'id' not in request.json: 
     response.status = 400 
     return {'error': 'ValidationError', 'error_message': '`id` required'} 
    db = {} # should be actual db cursor or whatever 
    if 'id' not in db: 
     response.status = 404 
     return {'error': 'Not Found', 
       'error_message': 'Foo `id` "{id}" not found'.format(id)} 
    body = request.json 
    # persist to db, return updated object 
    # another try/catch here in case of update error (from ORM and/or db) 
    return body 

Jednym ze sposobów rozwiązania tego problemu jest stworzenie globalnej obsługi błędów i podnieść błędy w każdym miejscu.

Innym jest użycie dekoratorów, które również mają problemy z napowietrznymi.

Czy istnieje lepszy sposób sprawdzania poprawności każdej trasy? - Mam na myśli coś takiego:

foo_app.post('/foo', middleware=[HAS_BODY_F, ID_IN_DB_F]) 
+0

Wystarczy ciekawy (ponieważ używam ich wszystkich na miejscu) - masz żadnych odniesień do«dekoratorów ... mają problemy napowietrznych»? –

+0

Nie pamiętam dokładnie, kiedy o tym usłyszałem, ale oto blog na ten temat: http://blog.dscpl.com.au/2014/02/performance-overhead-when-applying.html –

+0

Dzięki, doceń odniesienie. –

Odpowiedz

2

skończyło się znalezieniem wbudowany „middleware” butelki o nazwie „plugins "(reference):

from bottle import Bottle, request, response 

app = Bottle() 


def has_body(f): 
    def inner(*args, **kwargs): 
     if request.json: 
      return f(*args, **kwargs) 

     response.status = 400 
     return {'error': 'ValidationError', 
       'error_message': 'Body is required (and must be JSON).'} 
    return inner 


def body_req(required): 
    def body_req_middleware(f): 
     def inner(*args, **kwargs): 
      intersection = required.intersection(set(request.json.keys())) 
      if intersection != required: 
       response.status = 400 
       return {'error': 'ValidationError', 
         'error_message': 'Key(s): {} are not in JSON payload' 
         ''.format(', '.join('{!r}'.format(k) 
              for k in required - intersection))} 
      return f(*args, **kwargs) 
     return inner 
    return body_req_middleware 


@app.post('/foo', apply=(has_body, body_req({'id', 'f'}))) 
def get_foo(): 
    return {'foo': 'bar'} 
0

Oto moje rozwiązanie do wyschnięcia walidacji, skończyło się z dekoratora:

from itertools import imap, ifilter  
from bottle import Bottle, request, response 

app = Bottle() 

middleware = lambda functions: lambda caller: lambda *args, **kwargs: next(
    ifilter(None, imap(lambda g: g(*args, **kwargs), functions)), 
    caller(*args, **kwargs) 
) 


def has_body(*args, **kwargs): 
    if not request.json: 
     response.status = 400 
     return {'error': 'ValidationError', 
       'error_message': 'Body is required (and must be JSON).'} 


def body_req(required): 
    def inner(*args, **kwargs): 
     intersection = required.intersection(set(request.json.keys())) 
     if intersection != required: 
      response.status = 400 
      return {'error': 'ValidationError', 
        'error_message': 'Key(s): {} are not in JSON payload'.format(
        ', '.join(imap(lambda key: "'{key}'".format(key=key), 
           required - intersection)))} 

    return inner 


@app.post('/foo') 
@middleware([has_body, body_req({'id', 'f'})]) 
def get_foo(): 
    return {'foo': 'bar'}