在GCP功能中使用烧瓶布线?

时间:2018-11-26 20:44:44

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

我希望使用python从单个GCP云功能提供多个路由。虽然GCP功能实际上在内部使用了Flask,但我似乎无法弄清楚如何使用Flask路由系统通过单个云功能为多个路由提供服务。

我当时在做一个很小的项目,所以我写了一个自己的快速路由器,效果很好。现在,我更多地使用了GCP功能,我想弄清楚如何使用Flask路由器,或者在我的手动版本和开放源代码上投入更多的时间,尽管当它看起来非常实用时似乎显得多余了。烧瓶路由的紧密副本,因此如果不存在此功能,最好将其直接添加到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)

5 个答案:

答案 0 :(得分:1)

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

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

我正在使用VS-Code,现在可以按F5打开app.py,选择“调试当前文件”。现在,我可以在函数main.py中设置断点了。我安装了“ REST客户端”扩展程序,它使我能够配置GET和POST调用,这些调用可以针对本地URL和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)

答案 1 :(得分:1)

@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)

答案 2 :(得分:0)

以下解决方案对我有用:

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/

答案 3 :(得分:0)

感谢@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)

答案 4 :(得分:0)

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 使用,所以它也应该在那里工作。