将 FCM 令牌订阅到 Cloud Function 中的主题时 HTTP 函数超时
HTTP function times out when subscribing an FCM token to a topic in Cloud Function
最小可重现代码:
index.ts
:
import * as admin from "firebase-admin"
import fetch, { Headers } from "node-fetch";
interface BarPayload {
topic: string,
token: string,
}
exports.bar = functions.https.onCall(async (data, context) => {
if (data != null) {
const payload: BarPayload = {
topic: data.topic,
token: data.token,
}
const url = `https://${location}-${project}.cloudfunctions.net/subscribeToTopic`
await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
topic: payload.topic,
token: payload.token,
}),
})
}
return null;
});
export const subscribeToTopic = functions.https.onRequest(async (req, res) => {
const payload = req.body as BarPayload;
fetch('https://iid.googleapis.com/iid/v1/' + payload.token + '/rel/topics/' + payload.topic, {
method: 'POST',
headers: new Headers({
'Authorization': 'key=AA...Wp9',
'Content-Type': 'application/json'
})
}).then(response => {
if (response.status < 200 || response.status >= 400) {
res.sendStatus(299)
}
}).catch(error => {
console.error(error);
res.sendStatus(299)
})
return Promise.resolve();
})
我在 Flutter 中 运行 bar
,我在 Logs Explorer 中看到超时错误:
textPayload: "Function execution took 60051 ms. Finished with status: timeout"
但是如果我将 subscribeToTopic
从 HTTP 函数更改为可调用函数,那么它就可以正常工作。例如:
exports.subscribeToTopic = functions.https.onCall(async (data, context) => {
fetch('https://iid.googleapis.com/iid/v1/' + data.token + '/rel/topics/' + data.topic, {
method: 'POST',
headers: new Headers({
'Authorization': 'key=AA...Wp9',
'Content-Type': 'application/json'
})
}).then(response => {
if (response.status < 200 || response.status >= 400) {
console.log('Error = ' + response.error);
}
}).catch(error => {
console.error(error);
})
return null;
});
(我知道我犯了一些小错误,而且我是 Typescript 的新手。任何帮助将不胜感激:)
您不应该在 HTTPS 云功能中执行 return Promise.resolve();
:
- HTTPS 云函数应以
send()
、redirect()
或 end()
; 终止
return Promise.resolve();
在异步调用 fetch 完成之前执行。
以下应该可以解决问题(未经测试):
export const subscribeToTopic = functions.https.onRequest(async (req, res) => {
try {
const payload = req.body as BarPayload;
const response = await fetch('https://iid.googleapis.com/iid/v1/' + payload.token + '/rel/topics/' + payload.topic, {
method: 'POST',
headers: new Headers({
'Authorization': 'key=AA...Wp9',
'Content-Type': 'application/json'
})
});
if(response.status < 200 || response.status >= 400) {
res.status(299).send();
}
} catch (error) {
res.status(400).send();
}
})
但是我不明白为什么您将业务逻辑分离到两个 Cloud Functions 中。为什么不直接在 bar
可调用云函数中获取 https://iid.googleapis.com
?
最小可重现代码:
index.ts
:
import * as admin from "firebase-admin"
import fetch, { Headers } from "node-fetch";
interface BarPayload {
topic: string,
token: string,
}
exports.bar = functions.https.onCall(async (data, context) => {
if (data != null) {
const payload: BarPayload = {
topic: data.topic,
token: data.token,
}
const url = `https://${location}-${project}.cloudfunctions.net/subscribeToTopic`
await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
topic: payload.topic,
token: payload.token,
}),
})
}
return null;
});
export const subscribeToTopic = functions.https.onRequest(async (req, res) => {
const payload = req.body as BarPayload;
fetch('https://iid.googleapis.com/iid/v1/' + payload.token + '/rel/topics/' + payload.topic, {
method: 'POST',
headers: new Headers({
'Authorization': 'key=AA...Wp9',
'Content-Type': 'application/json'
})
}).then(response => {
if (response.status < 200 || response.status >= 400) {
res.sendStatus(299)
}
}).catch(error => {
console.error(error);
res.sendStatus(299)
})
return Promise.resolve();
})
我在 Flutter 中 运行 bar
,我在 Logs Explorer 中看到超时错误:
textPayload: "Function execution took 60051 ms. Finished with status: timeout"
但是如果我将 subscribeToTopic
从 HTTP 函数更改为可调用函数,那么它就可以正常工作。例如:
exports.subscribeToTopic = functions.https.onCall(async (data, context) => {
fetch('https://iid.googleapis.com/iid/v1/' + data.token + '/rel/topics/' + data.topic, {
method: 'POST',
headers: new Headers({
'Authorization': 'key=AA...Wp9',
'Content-Type': 'application/json'
})
}).then(response => {
if (response.status < 200 || response.status >= 400) {
console.log('Error = ' + response.error);
}
}).catch(error => {
console.error(error);
})
return null;
});
(我知道我犯了一些小错误,而且我是 Typescript 的新手。任何帮助将不胜感激:)
您不应该在 HTTPS 云功能中执行 return Promise.resolve();
:
- HTTPS 云函数应以
send()
、redirect()
或end()
; 终止
return Promise.resolve();
在异步调用 fetch 完成之前执行。
以下应该可以解决问题(未经测试):
export const subscribeToTopic = functions.https.onRequest(async (req, res) => {
try {
const payload = req.body as BarPayload;
const response = await fetch('https://iid.googleapis.com/iid/v1/' + payload.token + '/rel/topics/' + payload.topic, {
method: 'POST',
headers: new Headers({
'Authorization': 'key=AA...Wp9',
'Content-Type': 'application/json'
})
});
if(response.status < 200 || response.status >= 400) {
res.status(299).send();
}
} catch (error) {
res.status(400).send();
}
})
但是我不明白为什么您将业务逻辑分离到两个 Cloud Functions 中。为什么不直接在 bar
可调用云函数中获取 https://iid.googleapis.com
?