Flask-RESTful API:多个复杂的端点
Posted
技术标签:
【中文标题】Flask-RESTful API:多个复杂的端点【英文标题】:Flask-RESTful API: multiple and complex endpoints 【发布时间】:2014-01-10 00:02:02 【问题描述】:在我的 Flask-RESTful API 中,假设我有两个对象,用户和城市。这是一对多的关系。现在,当我创建我的 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')
如您所见,我可以做任何我想做的事情来实现一个非常基本的功能。我可以 GET、POST、PUT 和 DELETE 两个对象。但是,我的目标有两个:
(1) 能够使用城市名称等其他参数进行请求,而不仅仅是
城市标识。它看起来像:api.add_resource(CityAPI, '/api/city/<string:name>', endpoint='city')
除非它不会给我这个错误:
AssertionError:视图函数映射正在覆盖现有的 端点函数
(2) 能够将两个资源组合在一个请求中。说我想得到所有
与某个城市相关联的用户。在 REST URL 中,它应该类似于:/api/cities/<int:id>/users
我如何用 Flask 做到这一点?我将其映射到哪个端点?
基本上,我正在寻找将我的 API 从基本变为有用的方法。
【问题讨论】:
【参考方案1】:你犯了两个错误。
首先,Flask-RESTful 让您认为资源是使用单个 URL 实现的。实际上,您可以有许多不同的 URL,它们返回相同类型的资源。在 Flask-RESTful 中,您需要为每个 URL 创建一个不同的 Resource
子类,但从概念上讲,这些 URL 属于同一个资源。请注意,事实上,您已经为每个资源创建了两个实例来处理列表和单个请求。
您犯的第二个错误是您希望客户端知道您 API 中的所有 URL。这不是构建 API 的好方法,理想情况下,客户端只知道几个*** URL,然后从*** URL 的响应中的数据中发现其余部分。
在您的 API 中,您可能希望将 /api/users
和 /api/cities
公开为*** API。各个城市和用户的 URL 将包含在响应中。例如,如果我调用 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
,正如您定义的那样。
如果您还需要按城市名称搜索城市,您可以扩展“城市”端点来执行此操作。例如,您可以让/api/cities/<name>
形式的 URL 返回与<name>
给出的搜索词匹配的城市列表。
使用 Flask-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')
我希望这会有所帮助!
【讨论】:
太好了,感谢您的修复。也感谢隐藏我的大部分 API 的建议 虽然 Miguel 几乎完全正确,但我认为重要的是要指出“[期望客户端知道 API 中的所有 URL] 并不是构建 API 的好方法”对此事的一种意见。预先定义 URL 不会导致语法错误,也不会导致应用程序崩溃;这是一个设计决定。是使用已定义的 URL 列表更好,还是仅使用带有指向 API 子级别的 URL 的*** API(或其他解决方案)仍然存在争议,正确的答案可能仍然是项目- 特定的。 @MarkHildreth:同意。 这个主题很有趣。您认为只有我打算使用此 API 的事实会有所不同吗?那么,如果此 API 用于我不打算向其他人公开的移动应用程序,这意味着我可以安全地公开我的所有 URL? 是否公开 API 不是安全问题,而是封装问题。你公开的越多,你将来改变的可能性就越小。此外,客户端必须从 ID 构建 URL,而不是在服务器中使用url_for()
构建 URL 并将它们传递给准备使用的客户端。【参考方案2】:
你可以在不复制资源的情况下做 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(name_or_id)
else:
city = CityModel.find_by_name(name_or_id)
if city:
return city.to_json(), 200
return 'error': 'not found', 404
不确定是否有任何负面影响。
【讨论】:
以上是关于Flask-RESTful API:多个复杂的端点的主要内容,如果未能解决你的问题,请参考以下文章
python 具有Flask-Restful的基本API服务器
Flask-Restful 中的中止方法忽略 CORS 选项