Fiole RESTful API paramètres multiples et complexes

dans mon API Flask-RESTful, imaginez que j'ai deux objets, users et cities. C'est une relation de 1 pour beaucoup. Maintenant, lorsque je crée mon API et y ajoute des ressources, tout ce que je peux faire semble être de les mapper très facilement et leur donner des URLs générales. Voici le code (avec des trucs inutiles non inclus):

class UserAPI(Resource):  # The API class that handles a single user
  def __init__(self):
    # Initialize

  def get(self, id):
    # GET requests

  def put(self, id):
    # PUT requests

  def delete(self, id):
    # DELETE requests

class UserListAPI(Resource):  # The API class that handles the whole group of Users
  def __init__(self):

  def get(self):

  def post(self):

api.add_resource(UserAPI, '/api/user/<int:id>', endpoint='user')
api.add_resource(UserListAPI, '/api/users/', endpoint='users')

class CityAPI(Resource):
  def __init__(self):

  def get(self, id):

  def put(self, id):

  def delete(self, id):

class CityListAPI(Resource):
  def __init__(self):

  def get(self):

  def post(self):

api.add_resource(CityListAPI, '/api/cities/', endpoint='cities')
api.add_resource(CityAPI, '/api/city/<int:id>', endpoint='city')

comme vous pouvez le voir, je peux faire tout ce que je veux pour implémenter des fonctionnalités très basiques. Je peux get, post, put et delete à la fois des objets. Cependant, mon objectif est double:

(1) Pour être en mesure pour demander avec d'autres paramètres comme le nom de la ville au lieu de juste la ville de id. Il ressemblerait à quelque chose comme:

api.add_resource(CityAPI, '/api/city/<string:name>', endpoint='city')

sauf qu'il ne me lancerait pas cette erreur:

AssertionError: la cartographie de la fonction de vue écrase une fonction existante point de terminaison de la fonction

(2) pouvoir combiner les deux ressources dans une demande. Dire que je voulais obtenir tous les les utilisateurs associés à une ville. Dans les URLs REST, il devrait regarder quelque chose comme:

/api/cities/<int:id>/users

comment je fais ça avec flasque? De ce point de terminaison dois-je carte?

en gros, je cherche des moyens de faire passer mon API de basic à utilisable. Merci pour toutes les idées/conseils

26
demandé sur Vishvajit Pathak 2013-12-21 07:57:41

2 réponses

vous faites deux erreurs.

tout d'abord, Flask-RESTful vous amène à penser qu'une ressource est implémentée avec une URL unique. En réalité, vous pouvez avoir beaucoup D'URLs différentes qui renvoient des ressources du même type. Dans Flask-RESTful vous aurez besoin de créer un différent Resource sous-classe pour chaque URL, mais conceptuellement ces URLs appartiennent à la même ressource. Notez que vous avez, en fait, créé deux instances par ressource déjà pour gérer la liste et la personne demande.

la deuxième erreur que vous faites est que vous vous attendez à ce que le client connaisse toutes les URLs de votre API. Ce n'est pas une bonne façon de construire des API, idéalement le client ne connaît que quelques URLs de haut niveau et découvre ensuite le reste à partir des données des réponses de haut niveau.

dans votre API vous pouvez vouloir exposer le /api/users et /api/cities comme APIs de haut niveau. Les adresses URL des différentes villes et des utilisateurs seront incluses dans les réponses. Par exemple, si je invoquer http://example.com/api/users pour obtenir la liste des utilisateurs que je puisse obtenir cette réponse:

{
    "users": [ 
        {
            "url": "http://example.com/api/user/1",
            "name": "John Smith",
            "city": "http://example.com/api/city/35"
        },
        {
            "url": "http://example.com/api/user/2",
            "name": "Susan Jones",
            "city": "http://example.com/api/city/2"
        }
    ]
}

notez que la représentation JSON d'un utilisateur inclut L'URL de cet utilisateur, ainsi que l'URL de la ville. Le client n'a pas besoin de savoir comment construire ces, parce qu'ils lui sont données.

Pour les villes par leur nom

L'URL D'une ville est /api/city/<id>, et l'URL pour obtenir la liste complète des villes est /api/cities, comme vous l'avez défini.

Si vous aussi besoin de rechercher des villes par leur nom, vous pouvez étendre le point final "villes" pour le faire. Par exemple, vous pouvez avoir des URLs dans le formulaire /api/cities/<name> retournez la liste de villes dont le terme de recherche correspond à <name>.

avec Flask-RESTful vous devrez définir un nouveau Resource sous-classe pour que, par exemple:

    class CitiesByNameAPI(Resource):
        def __init__(self):
            # ...    
        def get(self, name):
            # ...

    api.add_resource(CitiesByNameAPI, '/api/cities/<name>', endpoint = 'cities_by_name')

Obtenir tous les utilisateurs qui appartiennent à un ville

Lorsque le client demande une ville, il devrait obtenir une réponse qui comprend une URL pour obtenir les utilisateurs dans cette ville. Par exemple, disons qu'à partir de la /api/users réponse ci-dessus, je veux savoir sur la ville de le premier utilisateur. Alors maintenant j'envoie une demande à http://example/api/city/35, et j'obtiens ce qui suit réponse JSON:

{
    "url": "http://example.com/api/city/35",
    "name": "San Francisco",
    "users": "http://example/com/api/city/35/users"
}

maintenant j'ai la ville, et ça m'a donné une URL que je peux utiliser pour avoir tous les utilisateurs de cette ville.

notez Qu'il n'est pas important que vos URLs soient laides ou difficiles à construire, parce que le client n'a jamais besoin de construire la plupart des ceux-ci à partir de zéro, il vient de les obtenir du serveur. Cela vous permet également de changer le format des URLs dans le futur.

pour implémenter L'URL qui permet aux utilisateurs par ville, vous ajoutez encore un autre Resource sous-classe de:

    class UsersByCityAPI(Resource):
        def __init__(self):
            # ...    
        def get(self, id):
            # ...

    api.add_resource(UsersByCityAPI, '/api/cities/<int:id>/users', endpoint = 'users_by_city')

j'espère que cela aide!

56
répondu Miguel 2013-12-21 19:32:47

Vous pouvez faire la chose id/name sans dupliquer la ressource:

api.add_resource(CitiesByNameAPI, '/api/cities/<name_or_id>', endpoint = 'cities_by_name')

class CitiesByNameAPI(Resource):
    def get(self, name_or_id):
        if name_or_id.isdigit():
            city = CityModel.find_by_id()
        else:
            city = CityModel.find_by_name()

        if city:
            return city.to_json(), 200
        return {'error': 'not found'}, 404

Je ne suis pas sûr que cela ait des effets négatifs.

0
répondu lciamp 2018-09-13 23:04:39