2013-07-18 8 views
10

staram się wystawiać API do różnych metod żądanie (GET, url x-www-form-urlencoded post i json POST):Lepszy sposób akceptowania wielu typów żądań w metodzie widoku pojedynczego?

@app.route('/create', methods=['GET', 'POST']) 
def create_file(): 
    if request.method == 'GET': 
     n = request.args.get('n') 
     t = request.args.get('t') 
    if request.method == 'POST': 
     if request.json: 
      n = request.json['n'] 
      t = request.json['t'] 
     else: 
      n = request.form['n'] 
      t = request.form['t'] 
    try: 
     n = int(n) 
    except: 
     n = 1 
    ... 

Powyższe wydaje się zbyt rozwlekły. Czy istnieje prostszy lub lepszy sposób na zapisanie tego? Dzięki.

+2

Zazwyczaj przenosimy moje metody API do osobnego schematu niż moje metody sieciowe. W ten sposób funkcjonalność nie zakłóca się nawzajem i pozwala na łatwiejszą konserwację. Chciałbym również spojrzeć na [flask-RESTful] (http://flask-restful.readthedocs.org/en/latest/) – sean

+0

Będą musiały spojrzeć dalej w projekty. Na razie zdecydowałem się na odpowiedź @ Miguela. Dzięki. – iandexter

Odpowiedz

14

Czy to wygląda lepiej? Moim zdaniem jest to nieco czystsze, jeśli możesz zaakceptować przeniesienie żądania JSON POST na inną trasę (co tak naprawdę powinieneś zrobić).

def _create_file(n, t): 
    try: 
     n = int(n) 
    except: 
     n = 1 
    ... 

@app.route('/create') 
def create_file(): 
    n = request.args.get('n') 
    t = request.args.get('t') 
    return _create_file(n, t) 

@app.route('/create', methods = ['POST']) 
def create_file_form(): 
    n = request.form.get('n') 
    t = request.form.get('t') 
    return _create_file(n, t) 

@app.route('/api/create', methods = ['POST']) 
def create_file_json(): 
    if not request.json: 
     abort(400); # bad request 
    n = request.json.get('n') 
    t = request.json.get('t') 
    return _create_file(n, t) 
+0

Tak, właśnie to wybrałem. Dzięki. – iandexter

4
Nie

jest nic Cię powstrzymuje od przepisanie kodu na:

@app.route('/create', methods=['GET', 'POST']) 
def create_file(): 
    params = None 
    if request.method == 'GET': 
     params = request.args 
    if request.method == 'POST': 
     if request.json: 
      params = request.json 
     else: 
      params = request.form 

    n = params.get('n') 
    t = params.get('t') 

    try: 
     n = int(n) 
    except: 
     n = 1 
    ... 
+0

To naprawdę nie ma nic lepszego. [Negocjacja treści] (https://en.wikipedia.org/wiki/Content_negotiation) nie jest rzeczą, którą można łatwo zrobić za pomocą Flask (IMHO). Domyślnie żądania są wywoływane tylko przez adres URL i metodę.Nie wiem, ile pracy wymaga wysłanie przez wartość nagłówka. – dAnjou

+0

Następnie defenetly powinien wyglądać na [flask-RESTful] (http://flask-restful.readthedocs.org/en/latest/) – twil

+1

Na pewno * nie * to zrobić. Wolę używać https://github.com/nickstenning/negotiate, który ma mniej niż 150LoC. – dAnjou

0

Użyj rozszerzenia Flask-Restful zgodnie z sugestiami innych osób. Następnie można po prostu zrobić coś takiego:

class CreateFile(Resource): 
    def get(self): 
     args = parser.parse_args() 
     n,t = args['n'], args['t'] 

    def post(self, todo_id): 
     # do post stuff 

i tak dalej ...

+0

Jak to wpływa na skrócenie czasu negocjacji treści? – dAnjou

+0

To tylko sugestia. PO domaga się "prostszej lub lepszej drogi", niekoniecznie krótszej. – codegeek

+0

Zamień "krótszy" na "prostszy lub lepszy". – dAnjou

0

Nie wiem, czy to, czego szukasz, ponieważ już zaakceptowane odpowiedź, ale można usunąć wiele gadatliwości, stosując standardowe techniki refaktoryzacji. Po pierwsze, jeden odpowiedzialność dla każdej funkcji:

@app.route('/create', methods=['GET', 'POST']) 
def create_file(): 
    n, t = get_nt_request_data() 
    return process_n(n) 

def get_nt_request_data(): 
    if request.method == 'GET': 
     return get_nt_query_params() 
    if request.method == 'POST': 
     if request.json: 
      return get_nt_json() 
     else: 
      return get_nt_form() 
    return n, t 

def get_nt_query_params(): 
    n = request.args.get('n') 
    t = request.args.get('t') 
    return n, t 

def get_nt_json(): 
    n = request.json['n'] 
    t = request.json['t'] 
    return n, t 

def get_nt_form(): 
    n = request.form['n'] 
    t = request.form['t'] 
    return n, t 

def process_n(n): 
    try: 
     n = int(n) 
    except: 
     n = 1 

teraz, to na pewno nie krócej, ale osobiście uważam, że jest znacznie jaśniejsze. Każda pojedyncza funkcja ma jasno określony cel i nie jest zagracona. Osobiście wprowadzę "n, t" do obiektu z dwoma polami, ale to zależy od ciebie i od tego, co działa dla twojej aplikacji. Krok 2: mamy tu dość oczywiste kopiowanie/wklejanie. Oczyśćmy to.

@app.route('/create', methods=['GET', 'POST']) 
def create_file(): 
    n, t = get_nt_request_data() 
    return process_n(n) 

def get_nt_request_data(): 
    if request.method == 'GET': 
     return get_nt_query_params() 
    if request.method == 'POST': 
     if request.json: 
      return get_nt_json() 
     else: 
      return get_nt_form() 
    return n, t 

def get_nt_query_params(): 
    return build_nt(request.args) 

def get_nt_json(): 
    return build_nt(request.json) 

def get_nt_form(): 
    return build_nt(request.form) 

def build_nt(resource): 
    return resource.get("n"), resource.get("t") 

def process_n(n): 
    try: 
     n = int(n) 
    except: 
     n = 1 

Teraz docieramy gdzieś! Ale jeśli zrobimy to dla 20 różnych zasobów (zakładając, że twoje różne punkty końcowe będą zgodne z podobnymi regułami dla czasowników HTTP), będziemy mieć garść funkcji get_xx_request_data, które w zasadzie robią to samo. Sparametryzujmy!

@app.route('/create', methods=['GET', 'POST']) 
def create_file(): 
    n, t = get_request_data(build_nt) 
    return process_n(n) 

def build_nt(resource): 
    return resource.get("n"), resource.get("t") 

def process_n(n): 
    try: 
     n = int(n) 
    except: 
     n = 1 

# in a shared module somewhere 
def get_request_data(builder): 
    if request.method == 'GET': 
     return builder(request.args) 
    if request.method == 'POST': 
     if request.json: 
      return builder(request.json) 
     else: 
      return builder(request.form) 
    return n, t 

Nawet 11 linii kodu dla punktu końcowego i funkcja współdzielona, ​​którą można ponownie wykorzystać dla innych. (Zakładam, że jest to koncepcyjnie podobne do tego, co robią dostępne frameworki, nie miałem okazji ich sprawdzić.)

Ostatnia uwaga: utworzenie pliku z żądaniem GET przyniesie ci trochę podniosłem brwi i prawdopodobnie jakieś dziwne błędy w zależności od tego, jak dużo kontroli nad klientami i pośredniczącymi proxy. GET ma być idempotent, więc klienci powinni być w stanie powtórzyć je wszystkie chcąc nie chcąc, nie oczekując żadnych zmian stanu na serwerze (i tworzenie czegoś jest zdecydowanie zmianą stanu). Teoretycznie, serwer proxy powinien móc ponownie odtwarzać polecenie GET po czemsie sieci, nawet nie informując pierwotnego klienta, że ​​próbował dwa razy, ale w praktyce nigdy nie sprawiał mi kłopotów.

Powiązane problemy