reactjs - 为什么我得到 fetch is not defined 错误?
问题描述
按照本教程,我尝试使用 commercejs webhook 使用 SendGrid 创建自定义电子邮件收据模板。我已将此 github 存储库上传到此
测试 Netlify 站点。主要代码是我很确定我的变量是正确的,仍然没有收到并可能发送收据电子邮件。检查 Netlify 功能电子邮件日志时说:/functions/email.js
.env
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 错误令人困惑。此外,我也对如何正确实现标头感到困惑,因为教程没有指定如何正确实现。所以我只是添加了这样的标题,而不知道这是否是这样做的方法:
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);
}
需要一些关于如何让此代码实际工作的建议,该教程似乎未完成,或者我可能误解了一些缩减细节。
更新(下面的工作代码)必须在 netlify 上使用 axios 而不是 node.fetch(感谢@hotcakedev)wehn deplodey。还对代码进行了其他更改,以使其与 commerce.js 一起使用(请参阅工作代码以获取详细信息)
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);
}
解决方案
如果你想使用 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);
...
推荐阅读
- android - 重定向到appsflyer OneLink?
- laravel-8 - Laravel 不能从组件类返回集合/数组到组件
- sql - 从 SQL 中其他列的表基的每个部分中选择最小和最大列值
- mern - 如何在 PAYU 中给出固定金额?
- javascript - 如果存在数据,则删除空数组的警告
- android - 错误运行 github 操作任务“:app:kaptDebugKotlin”执行失败
- python - TypeError:无法转换类型的对象
到张量。内容:[无,16、16384、128]。考虑将元素转换为支持的类型 - vim - 数学表达式中的 _ 也将此 _ 视为 vim/gvim 中斜体块的开头,并显示错误的不匹配错误
- python - CustomJS 回调上的散景布局更新
- google-bigquery - 从 Google Analytics 4(即 GA4)数据中导出 BigQuery Audiences 以在 Google Ads 中进行再营销