Облачная функция HTTP-запуска с планировщиком облака
У меня проблема с работой в облачном планировщике для моей облачной функции. Я создал работу со следующими параметрами:
Цель: HTTP
URL: мой URL триггера для облачной функции
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.
Однако, когда я беру это тело и использую его как триггерное событие в тестировании, я не получаю никаких ошибок. Так что я думаю, что проблема в представлении тела для моей работы, но я понятия не имел, как это исправить. Я буду очень рад за любую идею.
4 ответа
Отказ от ответственности: я пытался решить ту же проблему с помощью NodeJS, и я могу получить решение
Я понимаю, что это старый вопрос. Но я чувствовал, что стоит ответить на этот вопрос, поскольку я потратил почти 2 часа на то, чтобы найти ответ на этот вопрос.
Сценарий - 1: запуск облачной функции через облачный планировщик
- Функция не может прочитать сообщение в теле запроса.
Сценарий - 2: Запуск облачной функции через вкладку "Тест" в интерфейсе облачной функции
- Вызов функции всегда выполняется без ошибок.
Что я нашел?
- Когда процедура GCF выполняется через Cloud Scheduler, она отправляет заголовок
content-type
какapplication/octet-stream
. Из-за этого express js не может анализировать данные в теле запроса, когда облачный планировщик отправляет данные POST. - Но когда то же самое тело запроса используется для тестирования функции через интерфейс облачной функции, все работает нормально, потому что функция тестирования в интерфейсе отправляет заголовок.
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 исправит ее в ближайшее время.
Другой способ решения проблемы - это:
request.get_json(force=True)
Он заставляет синтаксический анализатор обрабатывать полезную нагрузку как json, получая Mimetype. Ссылка на документацию по колбе находится здесь
Я думаю, что это немного более сжато, чем другие предложенные решения.
Спасибо @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"
Один из обходных приемов, который вы можете использовать, - предоставить заголовок Content-Type, установленный на «application / json». Вы можете увидеть настройку здесь .