【问题标题】:Using Flask Routing in GCP Function?在 GCP 功能中使用 Flask 路由?
【发布时间】:2019-04-28 13:11:54
【问题描述】:

我希望使用 python 从单个 GCP 云函数中提供多个路由。虽然 GCP 函数实际上在后台使用了烧瓶,但我似乎无法弄清楚如何使用烧瓶路由系统从单个云函数中提供多个路由。

我正在做一个非常小的项目,所以我自己编写了一个运行良好的快速路由器。现在我更多地使用 GCP Functions,我要么想弄清楚如何使用 Flask 路由器,要么在我的手卷版本上投入更多时间,也许还开源它,尽管它看起来很多余关闭烧瓶路由的副本,因此如果此功能不存在,最好将其直接添加到烧瓶中。

有人对这个问题有任何经验吗?我猜我错过了一个隐藏在 Flask 中某处的简单函数,但如果不是,这似乎是一个相当大/常见的问题,尽管我猜 GCP Functions python 是 beta 是有原因的?

编辑: 如果可能的话,我想使用 Flask 的手卷版本的精简示例:

router = MyRouter()

@router.add('some/path', RouteMethod.GET)
def handle_this(req):
    ...


@router.add('some/other/path', RouteMethod.POST)
def handle_that(req):
    ...


# main entry point for the cloud function
def main(request):
    return router.handle(request)

【问题讨论】:

  • 大多数 Web 框架基于 URL 路径进行路由。由于您只能为每个 Google Cloud Function 提供一条路径,您希望使用什么来路由请求?
  • 我想在 URL 路径上进行路由,我用我的手卷版本成功地做到了。抱歉,不清楚。关键是,如果云功能在example.com/my-function/ 上运行,我希望理想情况下使用flask 为/my-function/a/my-function/b 路由,但目前我正在使用我自己的,因为我可以弄清楚如何使用Flask 的路由在GCP 功能,因为您没有运行应用程序,而只是获取 Flask.request 对象。基本上我喜欢一个函数,它接受那个请求对象并通过 Flask 的路由器运行它,虽然我找不到它。
  • 添加了一个例子来帮助澄清
  • 这听起来像是 stackoverflow.com/questions/51995682/… 的副本。我会坚持您现在拥有的自定义路由,或者将其迁移为 App Engine 应用程序(现在有一个 Python 3.7 环境,默认情况下也使用 Flask)
  • 考虑到 Flask 中 request 的全球背景,这是有道理的,只是希望隐藏着一些我没有找到的魔法。在这种情况下,云功能可以节省大量资金,所以我可能会开发自定义路由器并坚持使用该功能。珍惜时间!

标签: python-3.x flask google-cloud-platform google-cloud-functions


【解决方案1】:

Martin 的解决方案对我有用,直到我尝试在我的一条路线中调用 request.get_json()。最终结果是由于数据流已经被消耗,响应被阻塞在较低级别。

我在 Google Cloud Run 中寻找使用 functions_framework 的解决方案时遇到了这个问题。它已经设置了一个app,您可以通过从flask 导入current_app 来获得它。

from flask import current_app
app = current_app

我相信 functions_framework 被 Google Cloud Functions 使用,所以它也应该在那里工作。

【讨论】:

    【解决方案2】:

    @rabelenda 的简化版也适用于我:

    def main(request):
        with app.request_context(request.environ):
            try:
                rv = app.preprocess_request()
                if rv is None:
                    rv = app.dispatch_request()
            except Exception as e:
                rv = app.handle_user_exception(e)
            response = app.make_response(rv)
            return app.process_response(response)
    

    【讨论】:

      【解决方案3】:

      感谢 Guillaume Blaquiere 的 article 的启发和一些调整,我有了一种方法,使我能够使用 ngrok 生成一个公共 URL,用于本地测试和开发 Google Cloud Functions。

      我有两个关键文件,app.py 和 main.py。

      我正在使用 VS-Code,现在可以打开 app.py 按 F5,选择“调试当前文件”。现在我可以在我的函数 main.py 中设置断点。我安装了“REST 客户端”扩展,然后我可以配置 GET 和 POST 调用,我可以针对我的本地和 ngrok url 运行这些调用。

      #!/usr/bin/env python
      # -*- coding: utf-8 -*-
      #app.py
      
      import os
      from flask import Flask, request, Response
      from main import callback
      
      app = Flask(__name__)
      
      
      @app.route('/', methods=['GET', 'POST'])
      def test_function():
          return callback(request)
      
      
      def start_ngrok():
          from pyngrok import ngrok
      
          ngrok_tunnel = ngrok.connect(5000)
          print(' * Tunnel URL:', ngrok_tunnel.public_url)
      
      
      if __name__ == '__main__':
          if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
              start_ngrok()
          app.run(debug=True)
      
      
      #!/usr/bin/env python3
      # This file main.py can be run as a Google Cloud function and deployed with:
      # gcloud functions deploy callback  --runtime python38 --trigger-http --allow-unauthenticated
      from flask import Response
      import datetime
      
      now = datetime.datetime.now()
      
      
      def callback(request):
          if request.method == 'POST':  # Block is only for POST request
              print(request.json)
              return Response(status=200)
      
          return Response(f'''
                 <!doctype html><title>Hello from webhook</title>
                 <body><h1>Hello! </h1><p>{now:%Y-%m-%d %H:%M}</p>
                 </body></html>
                 ''', status=200)
      
      

      【讨论】:

        【解决方案4】:

        感谢@rabelenda 上面的回答启发了我的回答,它只是调整了 data/json 参数,并支持对 InternalServerError 未处理的异常处理程序的支持:

        import werkzeug.datastructures
        
        
        def process_request_in_app(request, app):
            # source: https://stackoverflow.com/a/55576232/1237919
            with app.app_context():
                headers = werkzeug.datastructures.Headers()
                for key, value in request.headers.items():
                    headers.add(key, value)
        
                data = None if request.is_json else (request.form or request.data or None)
        
                with app.test_request_context(method=request.method,
                                              base_url=request.base_url,
                                              path=request.path,
                                              query_string=request.query_string,
                                              headers=headers,
                                              data=data,
                                              json=request.json if request.is_json else None):
                    try:
                        rv = app.preprocess_request()
                        if rv is None:
                            rv = app.dispatch_request()
                    except Exception as e:
                        try:
                            rv = app.handle_user_exception(e)
                        except Exception as e:
                            # Fallback to unhandled exception handler for InternalServerError.
                            rv = app.handle_exception(e)
                    response = app.make_response(rv)
                    return app.process_response(response)
        

        【讨论】:

          【解决方案5】:

          以下解决方案对我有用:

          import flask
          import werkzeug.datastructures
          
          
          app = flask.Flask(__name__)
          
          
          @app.route('some/path')
          def handle_this(req):
              ...
          
          
          @app.route('some/other/path', methods=['POST'])
          def handle_that(req):
              ...
          
          
          def main(request):
              with app.app_context():
                  headers = werkzeug.datastructures.Headers()
                  for key, value in request.headers.items():
                      headers.add(key, value)
                  with app.test_request_context(method=request.method, base_url=request.base_url, path=request.path, query_string=request.query_string, headers=headers, data=request.data):
                      try:
                          rv = app.preprocess_request()
                          if rv is None:
                              rv = app.dispatch_request()
                      except Exception as e:
                          rv = app.handle_user_exception(e)
                      response = app.make_response(rv)
                      return app.process_response(response)
          

          基于http://flask.pocoo.org/snippets/131/

          【讨论】:

          猜你喜欢
          • 2016-03-11
          • 1970-01-01
          • 2021-05-01
          • 2020-11-13
          • 2021-09-05
          • 2022-07-07
          • 1970-01-01
          • 1970-01-01
          • 2023-04-09
          相关资源
          最近更新 更多