1

我正在 Flask 中创建一个新的 RESTful API,它应该接受给定对象的 GET(用于请求资源)和 PATCH(用于执行各种增量、非幂等更新)。问题是,修补的某些数据必须经过身份验证,而有些则不应该。

举个例子来说明这一点,假设我正在构建一个应用程序,让每个人都可以查询一个资源被点击了多少次,以及它的页面被查看了多少次。它还让人们在 javascript 中更新资源,说资源被再次点击(未经身份验证,因为它来自前端)。它还让经过身份验证的后端增加页面被查看的次数。

因此,遵循 RESTful 原则,我认为所有三个操作都应该在同一条路径上完成——比如/pages/some_page_name应该接受 GET 和 PATCH,并且应该接受两种不同类型的数据和 PATCH。问题是,在 Flask 中,看起来身份验证总是使用方法周围的装饰器完成,所以如果我有一个类似 的方法@app.route('/pages/<page_id>', methods=['GET', 'PATCH']),我的身份验证将使用@auth.login_required整个方法的装饰器完成,这甚至会强制方法不需要身份验证即可进行身份验证。

所以,我的问题是三个方面:

  1. 我在构建同一路径下提到的所有三个动作时是否正确/这很重要吗?
  2. 如果我是对的,这很重要,我如何只要求对一种类型的 PATCH 进行身份验证?
  3. 如果这不重要,那么构建此 API 的更好或更简单的方法是什么?
4

1 回答 1

1

我发现您的设计存在几个问题。

假设我正在构建一个应用程序,让每个人都可以查询一个资源被点击了多少次,以及它的页面被浏览了多少次

唔。这真的不是一个好的 REST 设计。您不能让客户查询选择资源的“属性”,只能查询资源本身。如果您的资源是“页面”,那么 GET 请求/pages/some_page_name应返回如下内容(在 JSON 中):

{
    'url': 'http://example.com/api/pages/some_page_name',
    'clicks': 35,
    'page_views': 102,
    <any other properties of a page resource here>
}

它还让人们在 javascript 中更新资源,说资源被再次点击

“点击某物”是一个动作,所以它不是一个好的 REST 模型。我对您的项目了解不够,所以我可能是错的,但我认为最好的解决方案是让用户单击该事物,然后服务器将收到某种请求(可能是获取资源的 GET被点击了?)。然后,服务器可以clicks自行增加资源的属性。

(未经身份验证,因为它来自前端)。

这可能很危险。如果您允许任何人更改您的资源,那么您很容易受到攻击,这可能是个问题。没有什么能阻止我查看您的 Javascript 并对您的 API 进行逆向工程,然后发送虚假请求以人为地更改计数器。这可能是可接受的风险,但请确保您了解这可能会发生。

它还让经过身份验证的后端增加页面被查看的次数。

后端?这是客户端还是服务器?听起来应该是客户。再一次,“递增”不适合 REST 类型的 API。让服务器根据从客户端收到的请求来管理计数器。

假设我明白你在说什么,在我看来你只需要支持GET。服务器可以在收到请求时自行更新这些计数器,客户端不需要为此烦恼。

更新:在下面的评论中提供了一些额外的信息之后,我认为你可以做的是 RESTful 也实现一个PUT请求(或者PATCH如果你是部分资源更新)。

如果您执行 a PUT,则客户端将发送与上述相同的 JSON 表示,但它将增加相应的计数器。您可以在服务器中添加验证以确保计数器按顺序递增,如果发现不是,则返回 400 状态代码(对于某些经过身份验证的用户,可能会跳过此验证,取决于您)。例如,从上面的例子开始,如果您需要增加点击次数(而不是页面浏览量),那么发送一个PUT请求:

{
    'url': 'http://example.com/api/pages/some_page_name',
    'clicks': 36,
    'page_views': 102
}

如果您正在使用PATCH,那么您可以删除不更改的项目:

{
    'clicks': 36
}

老实说,我觉得这不是解决您问题的最佳设计。您在这里有非常特定的客户端和服务器,它们旨在相互协作。REST 对于解耦的客户端和服务器来说是一个很好的设计,但是如果你站在这条线的两边,那么 REST 并不能真正给你很多。

现在关于您的身份验证问题,如果您的PUT/PATCH需要选择性地进行身份验证,那么您可以仅在必要时发出 HTTP Basic 身份验证交换。我写了Flask-HTTPAuth扩展,你可以看看我是如何实现这个交换的,并将代码复制到你的视图函数中,这样你就可以在必要时发出它。我希望这能澄清一些事情。

于 2015-01-03T18:45:12.173 回答