Flask RESTful API несколько и сложных конечных точек
в моем API-интерфейсе Flask-RESTful представьте, что у меня есть два объекта: пользователи и города. Это отношения "один ко многим". Теперь, когда я создаю свой API и добавляю к нему ресурсы, все, что я могу сделать, - это сопоставить им очень простые и общие URL-адреса. Вот код (с бесполезными вещами, не включенными):
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')
как вы можете видеть, я могу сделать все, что я хочу, чтобы реализовать базовую функциональность. Я могу получить, опубликовать, поместить и удалить оба объекта. Однако моя цель двоякая:
(1) Чтобы иметь возможность запрашивать с другими параметрами, такими как название города, а не просто
city id. Это будет выглядеть примерно так:api.add_resource(CityAPI, '/api/city/<string:name>', endpoint='city')
кроме того, он не бросил бы мне эту ошибку:
AssertionError: отображение функции просмотра перезаписывает существующий функция конечной точки
(2), чтобы иметь возможность объединить два ресурса в запросе. Скажи, что я хотел получить все
пользователи, связанные с каким-то городом. В REST URLs это должно выглядеть что-то например:/api/cities/<int:id>/users
как это сделать с фляжкой? К какой конечной точке я ее сопоставляю?
В основном, я ищу способы взять мой API от basic до useable. Спасибо за любые идеи/советы
2 ответов
Вы делаете две ошибки.
во-первых, Flask-RESTful заставляет вас думать, что ресурс реализован с одним URL-адресом. На самом деле у вас может быть много разных URL-адресов, возвращающих ресурсы одного типа. В Flask-RESTful вам нужно будет создать другой Resource
подкласс для каждого URL, но концептуально эти URL принадлежат одному и тому же ресурсу. Обратите внимание, что фактически вы уже создали два экземпляра для каждого ресурса для обработки списка и отдельного запросы.
вторая ошибка, которую вы делаете, заключается в том, что вы ожидаете, что клиент будет знать все URL-адреса в вашем API. Это не очень хороший способ создания API, в идеале клиент знает только несколько URL верхнего уровня, а затем обнаруживает остальные данные в ответах с верхнего уровня.
в вашем API вы можете выставить /api/users
и /api/cities
как API верхнего уровня. URL-адреса отдельных городов и пользователей будут включены в ответы. Например, если я invoke http://example.com/api/users
чтобы получить список пользователей, я могу получить этот ответ:
{
"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"
}
]
}
обратите внимание, что представление JSON пользователя включает URL для этого пользователя, а также URL для города. Клиенту не нужно знать, как их построить, потому что они ему даны.
получение городов по их названию
URL для города является /api/city/<id>
, и URL, чтобы получить полный список городов является /api/cities
, как вы его определили.
если вы также нужно искать города по их названию вы можете расширить конечную точку "города", чтобы сделать это. Например, у вас могут быть URL-адреса в форме /api/cities/<name>
верните список городов, которые соответствуют поисковому термину, заданному как <name>
.
с фляжкой-RESTful вам нужно будет определить новый Resource
подкласс для этого, например:
class CitiesByNameAPI(Resource):
def __init__(self):
# ...
def get(self, name):
# ...
api.add_resource(CitiesByNameAPI, '/api/cities/<name>', endpoint = 'cities_by_name')
получение всех пользователей, которые принадлежат к городу
когда клиент запрашивает город, он должен получить ответ, который включает в себя URL для пользователей в этом городе. Например, скажем, что из /api/users
ответ выше, я хочу узнать о городе из первых пользователей. Итак, теперь я посылаю запрос http://example/api/city/35
, и я получаю следующий ответ JSON:
{
"url": "http://example.com/api/city/35",
"name": "San Francisco",
"users": "http://example/com/api/city/35/users"
}
теперь у меня есть город, и что дал мне URL, который я могу использовать, чтобы получить всех пользователей в этом городе.
обратите внимание, что не имеет значения, что ваши URL-адреса уродливы или трудно построить, потому что клиенту никогда не нужно строить большинство это с нуля, он просто получает их с сервера. Это также позволяет изменять формат URL-адресов в будущем.
чтобы реализовать URL, который получает пользователей по городу, вы добавляете еще один Resource
подкласс:
class UsersByCityAPI(Resource):
def __init__(self):
# ...
def get(self, id):
# ...
api.add_resource(UsersByCityAPI, '/api/cities/<int:id>/users', endpoint = 'users_by_city')
надеюсь, это поможет!
вы можете сделать id / name вещь без дублирования ресурса:
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
Не уверен, есть ли какие-либо негативные последствия от этого.