使用 Cloud Scheduler 触发 Cloud Functions 的 HTTP

Posted

技术标签:

【中文标题】使用 Cloud Scheduler 触发 Cloud Functions 的 HTTP【英文标题】:HTTP Triggering Cloud Function with Cloud Scheduler 【发布时间】:2019-04-12 11:01:51 【问题描述】:

我的云功能在 Cloud Scheduler 中的作业有问题。我使用下一个参数创建了作业:

目标:HTTP

网址:我的云功能触发网址

HTTP 方法:POST

正文


 "expertsender": 
  "apiKey": "ExprtSender API key",
  "apiAddress": "ExpertSender APIv2 address",
  "date": "YYYY-MM-DD",
  "entities": [
     
        "entity": "Messages"
     ,
     
        "entity": "Activities",
        "types":[
           "Subscriptions"
        ]
     
  ]
 ,
 "bq": 
         "project_id": "YOUR GCP PROJECT",
         "dataset_id": "YOUR DATASET NAME",
         "location": "US"
        

这个身体的真实值已经改变了。

当我运行这个作业时,我遇到了一个错误。原因是处理来自 POST 请求的正文。

但是,当我将此主体用作测试中的触发事件时,我没有收到任何错误。所以我认为,我的工作中身体表现的问题,但我不知道如何解决它。有任何想法我都会很高兴。

【问题讨论】:

错误是什么?正文内容是什么? @DougStevenson 当我试图在云函数中获取身体数据时发生错误(我使用 python)。因此,我的函数获取了正文,但随后我在日志中看到:“NoneType”对象不可下标。这意味着,该函数无法正确提取主体中的参数,因为主体有问题。但是,当我从具有相同主体的云功能的测试界面触发我的功能时,不会发生错误。我在上面的一个问题中留下的正文内容。 您可能想要编辑您的问题以显示您的代码并指出发生错误的行。您还应该展示您期望的正文内容。 您能否也包括包含“'NoneType' 对象不可下标”的整个日志消息? @SergeyKravchenko 你能用更多细节更新这个问题吗? 【参考方案1】:

免责声明: 我尝试使用 NodeJS 解决相同的问题,并且我能够得到解决方案


我知道这是一个老问题。但我觉得值得回答这个问题,因为我花了将近 2 个小时来找出这个问题的答案。

场景一:通过云调度器触发云函数

函数无法读取请求正文中的消息。

场景二:通过 Cloud Function 界面中的 Test 选项卡触发 Cloud Function

函数调用始终可以正常执行,没有错误。

我发现了什么?

当 GCF 例程通过 Cloud Scheduler 执行时,它将标头 content-type 发送为 application/octet-stream。这使得在 Cloud scheduler POST 数据时 express js 无法解析请求体中的数据。 但是,当使用完全相同的请求正文通过 Cloud Function 接口测试函数时,一切正常,因为接口上的 Testing 功能将标头 content-type 发送为 application/json 和express js 能够读取请求正文并将数据解析为 JSON 对象。

解决方案

我必须手动将请求正文解析为 JSON(明确使用基于内容类型标头的 if 条件)以获取请求正文中的数据。

/**
 * Responds to any HTTP request.
 *
 * @param !express:Request req HTTP request context.
 * @param !express:Response res HTTP response context.
 */
exports.helloWorld = (req, res) => 
  let message = req.query.message || req.body.message || 'Hello World!';

  console.log('Headers from request: ' + JSON.stringify(req.headers));

  let parsedBody;

  if(req.header('content-type') === 'application/json') 
    console.log('request header content-type is application/json and auto parsing the req body as json');
    parsedBody = req.body; 
   else 
    console.log('request header content-type is NOT application/json and MANUALLY parsing the req body as json');
    parsedBody = JSON.parse(req.body);
  

  console.log('Message from parsed json body is:' + parsedBody.message);

  res.status(200).send(message);
;

这确实是 Google 必须解决的功能问题,希望 Google 尽快解决。

Cloud Scheduler - Content Type header issue

【讨论】:

在追查这个问题上做得很好!只是给未来访问者的一个快速说明,虽然您不能通过控制台设置标题(还),但如果您通过 gcloud 创建调度程序作业(例如gcloud scheduler jobs create ... --headers Content-Type=application/json ...),则可以设置标题。相关文档目前隐藏在this page 的模态中。 @ChadKruse 这是非常好的信息。我已经尝试过您的解决方案,它也可以正常工作。 cloud.google.com/sdk/gcloud/reference/beta/scheduler/jobs/…【参考方案2】:

感谢@Dinesh 指出请求标头作为解决方案!对于那些还在徘徊迷路的人,python 3.7.4中的代码:

import json

raw_request_data = request.data

# Luckily it's at least UTF-8 encoded...
string_request_data = raw_request_data.decode("utf-8")
request_json: dict = json.loads(string_request_data)

完全同意,从可用性的角度来看,这是低于标准的。让测试实用程序通过 JSON 并且发布“应用程序/八位字节流”的云调度程序是非常不负责任的设计。 但是,如果您想以不同的方式调用该函数,则应该创建一个请求处理程序:

def request_handler(request):
    # This works if the request comes in from 
    # requests.post("cloud-function-etc", json="key":"value")
    # or if the Cloud Function test was used
    request_json = request.get_json()
    if request_json:
        return request_json

    # That's the hard way, i.e. Google Cloud Scheduler sending its JSON payload as octet-stream
    if not request_json and request.headers.get("Content-Type") == "application/octet-stream":
        raw_request_data = request.data
        string_request_data = raw_request_data.decode("utf-8")
        request_json: dict = json.loads(string_request_data)

    if request_json:
        return request_json

    # Error code is obviously up to you
    else:
        return "500"

【讨论】:

【参考方案3】:

解决问题的另一种方法是:

request.get_json(force=True)

它强制解析器将有效负载视为 json,输入 Mimetype。 参考烧瓶文档是here

我认为这比其他建议的解决方案更简洁。

【讨论】:

【参考方案4】:

您可以使用的一种解决方法是将标头“Content-Type”设置为“application/json”。你可以看到一个设置here。

【讨论】:

以上是关于使用 Cloud Scheduler 触发 Cloud Functions 的 HTTP的主要内容,如果未能解决你的问题,请参考以下文章

如何将属性从 Cloud Scheduler 传递到 Pub/Sub?

尝试运行 Cloud Run 作业时,Cloud Scheduler 的权限被拒绝

Cloud Scheduler 不尊重我的超时设置(编辑:与 CloudRun 无关)

尝试运行 Google Cloud Scheduler 任务时出现 PERMISSION_DENIED 错误

spring-cloud-alibaba-sentinel和feign配合使用,启动报Caused by: java.lang.AbstractMethodError: com.alibaba.clo

Cloud Run 和 Cloud Scheduler - 在完整数据集上获取失败结果