【问题标题】:Structuring RESTful API in Flask when some methods require authentication and some don't当某些方法需要身份验证而有些方法不需要时,在 Flask 中构建 RESTful API
【发布时间】:2015-01-04 11:59:31
【问题描述】:

我正在 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 的更好或更简单的方法是什么?

【问题讨论】:

    标签: python rest authentication flask flask-security


    【解决方案1】:

    我发现您的设计存在一些问题。

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

    嗯。这真的不是一个好的 REST 设计。您不能让客户查询选择资源的“属性”,只能查询资源本身。如果您的资源是“页面”,那么对/pages/some_page_name 的 GET 请求应该返回类似这样的内容(在 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。服务器可以在收到请求时自行更新这些计数器,客户端无需为此烦恼。

    更新:在下面的 cmets 中提供了一些额外信息之后,我认为你可以做 RESTful 的方法是实现 PUT 请求(或者 PATCH,如果你不喜欢资源更新)。

    如果您执行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扩展,你可以看看我是如何实现这个交换的,把代码复制到你的视图函数中,这样你只有在需要的时候才能发出它。 我希望这能澄清一点。

    【讨论】:

    • 这是一个人为的例子。当然,在后端以安全、经过身份验证的方式做这些事情总是更好,但在我的用例中我无法做到这一点。我给出了一个简化的例子,它和我的用例有同样的困难。为了更好地说明这一点,请考虑一个像 MixPanel 这样的服务,它既可以支持来自后端的经过身份验证的调用,也可以支持来自 JavaScript 的未经身份验证的调用。我的用例是相似的,除了两个调用都在同一个资源上——只是所述资源的不同属性。
    • 至于 cmets 关于点击量不适合使用 RESTful API:想象一下您想要记录这些跨域的情况。为什么不使用 RESTful API 来记录这些?你会用什么代替?一般来说,我让外部客户与我提供的服务交谈,而 RESTful API 似乎最适合我。如果你不同意,我很想听听你的论点。
    • @Eli:当事实证明你过于简化你的情况时,真的很难给你建议。在支持未经身份验证的请求时,我并没有说这是错误的,只是警告您存在风险,以防您不知道它们。如果我正在做这样的事情,我至少会让客户端进行身份验证(而不是操作客户端的用户),但这完全取决于你,所以我不能说未经身份验证的调用是错误的,如果我给你印象他们是那么我应该更清楚,他们没有错。
    • 关于点击,我还是不明白。跨域是真实的情况还是您想提出示例?假设您有域 A 和 B。这些页面由 A 托管,而您的 API 由 B 托管。A 与页面一起提供的 Javascript 代码清楚地知道 B,因此 A 服务器可以轻松跟踪视图并通过无需使用 REST API 即可将它们发送到 B。
    • 我只是在寻找有关如何构建以 RESTful 方式提供我要求的功能的服务的建议,特别是在强制对某些 PATCH 调用而不是对其他调用进行身份验证方面,特别是在烧瓶。我很欣赏你关于为什么我想做的每件事都是一个坏主意的建议,但我只想知道如何去做。我希望这不会显得简洁或刻薄。
    猜你喜欢
    • 2014-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-29
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多