为什么我收到 fetch is not defined 错误?
Why I am getting the fetch is not defined error?
我已尝试使用commercejs webhooks通过SendGrid创建自定义电子邮件收据模板,方法是this tutorial. I have uploaded this github repository
this test Netlify site。主要代码是 /functions/email.js
我很确定我的 .env
变量是正确的,但仍然没有收到并可能发送的收据电子邮件。检查 Netlify 功能电子邮件日志时说:
5:55:03 PM: f7c003e5 ERROR Invoke Error {"errorType":"ReferenceError","errorMessage":"fetch is not defined","stack":["ReferenceError: fetch is not defined"," at Runtime.exports.handler (/var/task/email.js:30:22)"," at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"]}
5:55:03 PM: f7c003e5 Duration: 2.24 ms Memory Usage: 52 MB
但是我的 package.json
dependencies
看起来像这样:
"dependencies": {
"@chec/commerce.js": "2.2.0",
"@hookform/error-message": "0.0.5",
"@sendgrid/mail": "^7.4.7",
"@stripe/react-stripe-js": "1.1.2",
"@stripe/stripe-js": "1.11.0",
"autoprefixer": "10.0.4",
"classcat": "4.1.0",
"framer-motion": "2.9.4",
"next": "10.0.2",
"next-google-fonts": "1.2.1",
"node-fetch": "^3.0.0",
"pdf-lib": "^1.16.0",
"postcss": "8.1.14",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-hook-form": "6.11.5",
"react-toastify": "6.1.0",
"use-debounce": "^7.0.0"
},
因此我很困惑为什么我会收到 fetch is not defined 错误。另外,我也对如何正确实现 headers 感到困惑,因为教程没有具体说明如何实现。所以我只是像这样添加了 headers,不知道这是不是这样做的方法:
let headers = {
"Accept": "application/json",
"Content-Type": "application/json",
};
let sgheaders = {
Authorization: `Bearer ${process.env.SENDGRID_SECRET}`,
"Content-Type": "application/json",
};
在当前上传到我的 netlify 站点的代码中,我必须更改 export default async function handler(req, res) {
至
exports.handler = async function(req, res) {
根据 Netlify 函数文档 Netlify 函数文档。 (因为 "errorMessage": "SyntaxError: Unexpected token 'export'")
// Create the API endpoint function with a req and res parameter
exports.handler = async function(req, res) {
const checSecretAPIKey = process.env.CHEC_SECRET_KEY;
let headers = {
"Accept": "application/json",
"Content-Type": "application/json",
};
//export default async function handler(req, res) {
if (!req.body || req.httpMethod !== 'POST') {
return {
status: 405,
headers,
body: JSON.stringify({
status: 'Invalid HTTP method',
}),
}
}
const { data } = JSON.parse(req.body);
// Request for your merchant information so that you can use your email
// to include as the 'from' property to send to the SendGrid API
const merchant = fetch(`${process.env.CHEC_API_URL}/v1/merchants`, {
headers: {
'X-Authoriza†ion': process.env.CHEC_SECRET_KEY,
},
}).then((response) => response.json);
// Extract the signature from the registered `orders.create` webhook
const { signature } = data;
delete data.signature;
// Your Chec webhook signing key, from the Chec Dashboard webhooks view
const webHookSigningKey = 'KEJlxz6cIlrWIpsX5jypcMeGl2uh7jJg';
// Verify the signature
const expectedSignature = crypto.createHmac('sha256', webHookSigningKey)
.update(JSON.stringify(data))
.digest('hex');
if (expectedSignature !== signature) {
console.error('Signature mismatched, skipping.')
}
// Verify the age of the request to make sure it isn't more than 5 minutes old.
if (new Date(data.created * 1000) < new Date() - 5 * 60 * 1000) {
console.error('Webhook was sent too long ago, could potentially be fake, ignoring');
}
// Because you will need to list out the order line items, map through the returned line items
// and structure out the data you need to display in the email receipt for your customer
// Note that we are keeping the data structure minimal here
const orderLineItems = data.payload.order.line_items.map((lineItem) => ({
text: lineItem.product_name,
price: lineItem.line_total.formatted_with_symbol,
}));
// Signature is verified, continue to send data to SendGrid
// Create the email object payload to fire off to SendGrid
const emailPayload = {
to: data.payload.customer.email,
from: merchant.support_email,
subject: `Thank you for your order ${data.payload.customer.firstname}`,
text: `Your order number is ${data.payload.customer_reference}`,
// SendGrid expects a JSON blog containing the dynamic order data your template will use
// More information below in 'What's next?' on how to configure your dynamic template in SendGrid
// The property key names will depend on what dynamic template you create in SendGrid
dynamic_template_data: {
total: data.payload.order.subtotal.formatted_with_symbol,
items: orderLineItems,
receipt: true,
name: data.payload.shipping.name,
address01: data.payload.shipping.street,
city: data.payload.shipping.town_city,
state: data.payload.shipping.county_state,
zip : data.payload.shipping.postal_zip_code,
orderId : data.payload.id,
},
// In addition to specifying the dynamic template data, you need to specify the template ID. This comes from your SendGrid dashboard when you create you dynamic template
// https://mc.sendgrid.com/dynamic-templates
template_id: 'd-xxx'
}
let sgheaders = {
Authorization: `Bearer ${process.env.SENDGRID_SECRET}`,
"Content-Type": "application/json",
};
let response = {};
try {
// Call the SendGrid send mail endpoint
response = await sgMailClient.send(emailPayload);
return {
statusCode: 200,
headers: sgheaders,
body: 'Email sent!'
}
} catch (err) {
console.error('Error', err)
}
// Return the response from the request
return res.status(200).json(response);
}
需要一些关于如何让这段代码实际工作的建议,教程似乎还没有完成,或者我可能误解了一些缩减的细节。
更新(下面的工作代码)
必须使用 axios 而不是 node.fetch(感谢@hotcakedev)wehn deplodey on netlify。还对代码进行了其他更改,以使其与 commerce.js 一起使用(请参阅 detalis 的工作代码)
const axios = require('axios');
const sgMailClient = require("@sendgrid/mail");
sgMailClient.setApiKey(process.env.SENDGRID_API_KEY);
// Includes crypto module
const crypto = require('crypto');
// Create the API endpoint function with a req and res parameter
exports.handler = async function(req, res) {
//export default async function handler(req, res) {
if (!req.body || req.httpMethod !== 'POST') {
return {
status: 405,
headers: {},
body: JSON.stringify({
status: 'Invalid HTTP method',
}),
}
}
const data = JSON.parse(req.body);
// Request for your merchant information so that you can use your email
// to include as the 'from' property to send to the SendGrid API
const merchant = axios.get(`${process.env.CHEC_API_URL}/v1/merchants`, {
headers: {
'X-Authorization': process.env.CHEC_SECRET_KEY,
"Accept": "application/json",
"Content-Type": "application/json",
},
}).then((response) => response.json);
//console.log(data);
// Extract the signature from the registered `orders.create` webhook
const { signature } = data;
delete data.signature;
// Your Chec webhook signing key, from the Chec Dashboard webhooks view
const webHookSigningKey = 'KEJlxz6cIlrWIpsX5jypcMeGl2uh7jJg';
// Verify the signature
const expectedSignature = crypto.createHmac('sha256', webHookSigningKey)
.update(JSON.stringify(data))
.digest('hex');
if (expectedSignature !== signature) {
console.error('Signature mismatched, skipping.')
}
// Verify the age of the request to make sure it isn't more than 5 minutes old.
if (new Date(data.created * 1000) < new Date() - 5 * 60 * 1000) {
console.error('Webhook was sent too long ago, could potentially be fake, ignoring');
}
// Because you will need to list out the order line items, map through the returned line items
// and structure out the data you need to display in the email receipt for your customer
// Note that we are keeping the data structure minimal here
const orderLineItems = data.payload.order.line_items.map((lineItem) => ({
text: lineItem.product_name,
price: lineItem.line_total.formatted_with_symbol,
}));
// Signature is verified, continue to send data to SendGrid
// Create the email object payload to fire off to SendGrid
const emailPayload = {
to: data.payload.customer.email,
from: data.payload.merchant.support_email,
subject: `Thank you for your order ${data.payload.customer.firstname}`,
text: `Your order number is ${data.payload.customer_reference}`,
// SendGrid expects a JSON blog containing the dynamic order data your template will use
// More information below in 'What's next?' on how to configure your dynamic template in SendGrid
// The property key names will depend on what dynamic template you create in SendGrid
dynamic_template_data: {
total: data.payload.order.subtotal.formatted_with_symbol,
items: orderLineItems,
receipt: true,
name: data.payload.billing.name,
address01: data.payload.billing.street,
city: data.payload.billing.town_city,
state: data.payload.billing.county_state,
zip : data.payload.billing.postal_zip_code,
orderId : data.payload.id,
},
// In addition to specifying the dynamic template data, you need to specify the template ID. This comes from your SendGrid dashboard when you create you dynamic template
// https://mc.sendgrid.com/dynamic-templates
template_id: 'd-xxx'
};
/*let sgheaders = {
Authorization: `Bearer ${process.env.SENDGRID_SECRET}`,
"Content-Type": "application/json",
};*/
let response = {};
try {
// Call the SendGrid send mail endpoint
response = await sgMailClient.send(emailPayload);
return {
statusCode: 200,
headers: {},
body: JSON.stringify({
status: 'Email sent!',
}),
}
} catch (err) {
console.error('Error from function: ', err)
console.error(err.response.body);
console.log("Payload content: ", emailPayload );
}
// Return the response from the request
//return res.status(200).json(response);
}
此处为 Twilio SendGrid 开发人员布道师。
您已将node-fetch安装到项目中,但教程中没有要求将库添加到函数中才能使用。所以你需要 require node-fetch
.
本教程也未能要求 SendGrid 库和设置 API 键。您应该在 Netlify 的环境中设置 SendGrid API 键,名称类似于 SENDGRID_API_KEY
。然后将以下内容添加到函数的顶部:
const fetch = require("node-fetch");
const sgMailClient = require("@sendgrid/mail");
sgMailClient.setApiKey(process.env.SENDGRID_API_KEY);
至于您询问的 headers,它们是您的函数 return 响应传入 HTTP 请求的响应 headers。你 return 取决于你如何调用这个函数,但是你 不应该 return 你的 SendGrid API 键入 headers .
当您正在使用该功能时,我建议将 return headers 设置为空 object,直到您确定要设置的 Content-Type (响应 body 是“电子邮件已发送!”现在,这将是 text/plain
但这在前端并不是非常有用)以及您可能需要或可能不需要的其他 headers。
如果你想使用 Rest API 集成 sendgrid,我建议你使用 axios。
所以在你的情况下,
import axios from 'axios';
...
const merchant = axios.get(`${process.env.CHEC_API_URL}/v1/merchants`, {
headers: {
'X-Authoriza†ion': process.env.CHEC_SECRET_KEY,
},
}).then((response) => response.json);
...
我已尝试使用commercejs webhooks通过SendGrid创建自定义电子邮件收据模板,方法是this tutorial. I have uploaded this github repository
this test Netlify site。主要代码是 /functions/email.js
我很确定我的 .env
变量是正确的,但仍然没有收到并可能发送的收据电子邮件。检查 Netlify 功能电子邮件日志时说:
5:55:03 PM: f7c003e5 ERROR Invoke Error {"errorType":"ReferenceError","errorMessage":"fetch is not defined","stack":["ReferenceError: fetch is not defined"," at Runtime.exports.handler (/var/task/email.js:30:22)"," at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"]}
5:55:03 PM: f7c003e5 Duration: 2.24 ms Memory Usage: 52 MB
但是我的 package.json
dependencies
看起来像这样:
"dependencies": {
"@chec/commerce.js": "2.2.0",
"@hookform/error-message": "0.0.5",
"@sendgrid/mail": "^7.4.7",
"@stripe/react-stripe-js": "1.1.2",
"@stripe/stripe-js": "1.11.0",
"autoprefixer": "10.0.4",
"classcat": "4.1.0",
"framer-motion": "2.9.4",
"next": "10.0.2",
"next-google-fonts": "1.2.1",
"node-fetch": "^3.0.0",
"pdf-lib": "^1.16.0",
"postcss": "8.1.14",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-hook-form": "6.11.5",
"react-toastify": "6.1.0",
"use-debounce": "^7.0.0"
},
因此我很困惑为什么我会收到 fetch is not defined 错误。另外,我也对如何正确实现 headers 感到困惑,因为教程没有具体说明如何实现。所以我只是像这样添加了 headers,不知道这是不是这样做的方法:
let headers = {
"Accept": "application/json",
"Content-Type": "application/json",
};
let sgheaders = {
Authorization: `Bearer ${process.env.SENDGRID_SECRET}`,
"Content-Type": "application/json",
};
在当前上传到我的 netlify 站点的代码中,我必须更改 export default async function handler(req, res) {
至
exports.handler = async function(req, res) {
根据 Netlify 函数文档 Netlify 函数文档。 (因为 "errorMessage": "SyntaxError: Unexpected token 'export'")
// Create the API endpoint function with a req and res parameter
exports.handler = async function(req, res) {
const checSecretAPIKey = process.env.CHEC_SECRET_KEY;
let headers = {
"Accept": "application/json",
"Content-Type": "application/json",
};
//export default async function handler(req, res) {
if (!req.body || req.httpMethod !== 'POST') {
return {
status: 405,
headers,
body: JSON.stringify({
status: 'Invalid HTTP method',
}),
}
}
const { data } = JSON.parse(req.body);
// Request for your merchant information so that you can use your email
// to include as the 'from' property to send to the SendGrid API
const merchant = fetch(`${process.env.CHEC_API_URL}/v1/merchants`, {
headers: {
'X-Authoriza†ion': process.env.CHEC_SECRET_KEY,
},
}).then((response) => response.json);
// Extract the signature from the registered `orders.create` webhook
const { signature } = data;
delete data.signature;
// Your Chec webhook signing key, from the Chec Dashboard webhooks view
const webHookSigningKey = 'KEJlxz6cIlrWIpsX5jypcMeGl2uh7jJg';
// Verify the signature
const expectedSignature = crypto.createHmac('sha256', webHookSigningKey)
.update(JSON.stringify(data))
.digest('hex');
if (expectedSignature !== signature) {
console.error('Signature mismatched, skipping.')
}
// Verify the age of the request to make sure it isn't more than 5 minutes old.
if (new Date(data.created * 1000) < new Date() - 5 * 60 * 1000) {
console.error('Webhook was sent too long ago, could potentially be fake, ignoring');
}
// Because you will need to list out the order line items, map through the returned line items
// and structure out the data you need to display in the email receipt for your customer
// Note that we are keeping the data structure minimal here
const orderLineItems = data.payload.order.line_items.map((lineItem) => ({
text: lineItem.product_name,
price: lineItem.line_total.formatted_with_symbol,
}));
// Signature is verified, continue to send data to SendGrid
// Create the email object payload to fire off to SendGrid
const emailPayload = {
to: data.payload.customer.email,
from: merchant.support_email,
subject: `Thank you for your order ${data.payload.customer.firstname}`,
text: `Your order number is ${data.payload.customer_reference}`,
// SendGrid expects a JSON blog containing the dynamic order data your template will use
// More information below in 'What's next?' on how to configure your dynamic template in SendGrid
// The property key names will depend on what dynamic template you create in SendGrid
dynamic_template_data: {
total: data.payload.order.subtotal.formatted_with_symbol,
items: orderLineItems,
receipt: true,
name: data.payload.shipping.name,
address01: data.payload.shipping.street,
city: data.payload.shipping.town_city,
state: data.payload.shipping.county_state,
zip : data.payload.shipping.postal_zip_code,
orderId : data.payload.id,
},
// In addition to specifying the dynamic template data, you need to specify the template ID. This comes from your SendGrid dashboard when you create you dynamic template
// https://mc.sendgrid.com/dynamic-templates
template_id: 'd-xxx'
}
let sgheaders = {
Authorization: `Bearer ${process.env.SENDGRID_SECRET}`,
"Content-Type": "application/json",
};
let response = {};
try {
// Call the SendGrid send mail endpoint
response = await sgMailClient.send(emailPayload);
return {
statusCode: 200,
headers: sgheaders,
body: 'Email sent!'
}
} catch (err) {
console.error('Error', err)
}
// Return the response from the request
return res.status(200).json(response);
}
需要一些关于如何让这段代码实际工作的建议,教程似乎还没有完成,或者我可能误解了一些缩减的细节。
更新(下面的工作代码) 必须使用 axios 而不是 node.fetch(感谢@hotcakedev)wehn deplodey on netlify。还对代码进行了其他更改,以使其与 commerce.js 一起使用(请参阅 detalis 的工作代码)
const axios = require('axios');
const sgMailClient = require("@sendgrid/mail");
sgMailClient.setApiKey(process.env.SENDGRID_API_KEY);
// Includes crypto module
const crypto = require('crypto');
// Create the API endpoint function with a req and res parameter
exports.handler = async function(req, res) {
//export default async function handler(req, res) {
if (!req.body || req.httpMethod !== 'POST') {
return {
status: 405,
headers: {},
body: JSON.stringify({
status: 'Invalid HTTP method',
}),
}
}
const data = JSON.parse(req.body);
// Request for your merchant information so that you can use your email
// to include as the 'from' property to send to the SendGrid API
const merchant = axios.get(`${process.env.CHEC_API_URL}/v1/merchants`, {
headers: {
'X-Authorization': process.env.CHEC_SECRET_KEY,
"Accept": "application/json",
"Content-Type": "application/json",
},
}).then((response) => response.json);
//console.log(data);
// Extract the signature from the registered `orders.create` webhook
const { signature } = data;
delete data.signature;
// Your Chec webhook signing key, from the Chec Dashboard webhooks view
const webHookSigningKey = 'KEJlxz6cIlrWIpsX5jypcMeGl2uh7jJg';
// Verify the signature
const expectedSignature = crypto.createHmac('sha256', webHookSigningKey)
.update(JSON.stringify(data))
.digest('hex');
if (expectedSignature !== signature) {
console.error('Signature mismatched, skipping.')
}
// Verify the age of the request to make sure it isn't more than 5 minutes old.
if (new Date(data.created * 1000) < new Date() - 5 * 60 * 1000) {
console.error('Webhook was sent too long ago, could potentially be fake, ignoring');
}
// Because you will need to list out the order line items, map through the returned line items
// and structure out the data you need to display in the email receipt for your customer
// Note that we are keeping the data structure minimal here
const orderLineItems = data.payload.order.line_items.map((lineItem) => ({
text: lineItem.product_name,
price: lineItem.line_total.formatted_with_symbol,
}));
// Signature is verified, continue to send data to SendGrid
// Create the email object payload to fire off to SendGrid
const emailPayload = {
to: data.payload.customer.email,
from: data.payload.merchant.support_email,
subject: `Thank you for your order ${data.payload.customer.firstname}`,
text: `Your order number is ${data.payload.customer_reference}`,
// SendGrid expects a JSON blog containing the dynamic order data your template will use
// More information below in 'What's next?' on how to configure your dynamic template in SendGrid
// The property key names will depend on what dynamic template you create in SendGrid
dynamic_template_data: {
total: data.payload.order.subtotal.formatted_with_symbol,
items: orderLineItems,
receipt: true,
name: data.payload.billing.name,
address01: data.payload.billing.street,
city: data.payload.billing.town_city,
state: data.payload.billing.county_state,
zip : data.payload.billing.postal_zip_code,
orderId : data.payload.id,
},
// In addition to specifying the dynamic template data, you need to specify the template ID. This comes from your SendGrid dashboard when you create you dynamic template
// https://mc.sendgrid.com/dynamic-templates
template_id: 'd-xxx'
};
/*let sgheaders = {
Authorization: `Bearer ${process.env.SENDGRID_SECRET}`,
"Content-Type": "application/json",
};*/
let response = {};
try {
// Call the SendGrid send mail endpoint
response = await sgMailClient.send(emailPayload);
return {
statusCode: 200,
headers: {},
body: JSON.stringify({
status: 'Email sent!',
}),
}
} catch (err) {
console.error('Error from function: ', err)
console.error(err.response.body);
console.log("Payload content: ", emailPayload );
}
// Return the response from the request
//return res.status(200).json(response);
}
此处为 Twilio SendGrid 开发人员布道师。
您已将node-fetch安装到项目中,但教程中没有要求将库添加到函数中才能使用。所以你需要 require node-fetch
.
本教程也未能要求 SendGrid 库和设置 API 键。您应该在 Netlify 的环境中设置 SendGrid API 键,名称类似于 SENDGRID_API_KEY
。然后将以下内容添加到函数的顶部:
const fetch = require("node-fetch");
const sgMailClient = require("@sendgrid/mail");
sgMailClient.setApiKey(process.env.SENDGRID_API_KEY);
至于您询问的 headers,它们是您的函数 return 响应传入 HTTP 请求的响应 headers。你 return 取决于你如何调用这个函数,但是你 不应该 return 你的 SendGrid API 键入 headers .
当您正在使用该功能时,我建议将 return headers 设置为空 object,直到您确定要设置的 Content-Type (响应 body 是“电子邮件已发送!”现在,这将是 text/plain
但这在前端并不是非常有用)以及您可能需要或可能不需要的其他 headers。
如果你想使用 Rest API 集成 sendgrid,我建议你使用 axios。
所以在你的情况下,
import axios from 'axios';
...
const merchant = axios.get(`${process.env.CHEC_API_URL}/v1/merchants`, {
headers: {
'X-Authoriza†ion': process.env.CHEC_SECRET_KEY,
},
}).then((response) => response.json);
...