web-dev-qa-db-fra.com

Gestion des erreurs dans Python-MySQL

J'exécute un petit service Web basé sur python flask, où je veux exécuter une petite requête MySQL. Lorsque j'obtiens une entrée valide pour ma requête SQL, tout fonctionne comme prévu et j'obtiens le mais si la valeur n'est pas stockée dans la base de données, je reçois un TypeError

    Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1478, in full_dispatch_request
    response = self.make_response(rv)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1566, in make_response
    raise ValueError('View function did not return a response')
ValueError: View function did not return a response

J'ai essayé d'exploiter moi-même les erreurs et d'utiliser ce code pour mon projet, mais il semble que cela ne fonctionne pas correctement.

#!/usr/bin/python

from flask import Flask, request
import MySQLdb

import json

app = Flask(__name__)


@app.route("/get_user", methods=["POST"])
def get_user():
    data = json.loads(request.data)
    email = data["email"]

    sql = "SELECT userid FROM oc_preferences WHERE configkey='email' AND configvalue LIKE '" + email + "%';";

    conn = MySQLdb.connect( Host="localhost",
                            user="root",
                            passwd="ubuntu",
                            db="owncloud",
                            port=3306)
    curs = conn.cursor()

    try:
        curs.execute(sql)
        user = curs.fetchone()[0]
        return user
    except MySQLdb.Error, e:
        try:
            print "MySQL Error [%d]: %s" % (e.args[0], e.args[1])
            return None
        except IndexError:
            print "MySQL Error: %s" % str(e)
            return None
    except TypeError, e:
        print(e)
        return None
    except ValueError, e:
        print(e)
        return None
    finally:
        curs.close()
        conn.close()

if __name__ == "__main__":
    app.run(Host="0.0.0.0", port=5000, debug=True)

Fondamentalement, je veux simplement retourner une valeur, lorsque tout fonctionne correctement et que je ne veux rien retourner si ce n'est de préférence avec un message d'erreur sur mon serveur. Comment puis-je utiliser correctement la gestion des erreurs?

MODIFIER Code actuel mis à jour + message d'erreur.

12
BoJack Horseman

Premier point: vous avez trop de code dans votre bloc try/except. Mieux vaut utiliser des blocs try/except distincts lorsque vous avez deux instructions (ou deux groupes d'instructions) qui peuvent générer des erreurs différentes:

try:
    try:
        curs.execute(sql)
        # NB : you won't get an IntegrityError when reading
    except (MySQLdb.Error, MySQLdb.Warning) as e:
        print(e)
        return None

    try: 
        user = curs.fetchone()[0]
        return user
    except TypeError as e:
        print(e)
        return None

finally:
    conn.close()

Maintenant, devez-vous vraiment attraper une TypeError ici? Si vous lisez à la trace, vous remarquerez que votre erreur provient de l'appel de __getitem__() sur None (nb: __getitem__() est l'implémentation de l'opérateur d'indice []), Ce qui signifie que si vous n'avez pas de lignes correspondantes cursor.fetchone() renvoie None, vous pouvez donc simplement tester le retour de currsor.fetchone():

try:
    try:
        curs.execute(sql)
        # NB : you won't get an IntegrityError when reading
    except (MySQLdb.Error, MySQLdb.Warning) as e:
        print(e)
        return None

    row = curs.fetchone()
    if row:
        return row[0]
    return None

finally:
    conn.close()

Maintenant, avez-vous vraiment besoin d'attraper des erreurs MySQL ici? Votre requête est censée être bien testée et ce n'est qu'une opération de lecture, elle ne devrait donc pas planter - donc si vous avez quelque chose qui ne va pas ici, vous avez évidemment un problème plus important et vous ne voulez pas le cacher sous le tapis. IOW: enregistrez les exceptions (en utilisant le package logging et logger.exception()) standard et relancez-les ou plus simplement laissez-les se propager (et éventuellement, un composant de niveau supérieur s'occupe de la journalisation non gérée exceptions):

try:
    curs.execute(sql)
    row = curs.fetchone()
    if row:
        return row[0]
    return None

finally:
    conn.close()

Et enfin: la façon dont vous construisez votre requête SQL est absolument dangereux . Utilisez plutôt des espaces réservés sql:

q = "%s%%" % data["email"].strip() 
sql = "select userid from oc_preferences where configkey='email' and configvalue like %s"
cursor.execute(sql, [q,])
24