Я разработал бессерверное приложение, используя next.js, Vercel и Google Sheets. Я использую таблицы Google для сохранения контактных данных клиентов. Логика моего приложения отлично работает локально во время производства. Я успешно получаю данные в своем листе Google без каких-либо проблем. однако после развертывания я получаю следующую ошибку
ошибка - Ошибка [ERR_HTTP_HEADERS_SENT]: невозможно установить заголовки после их отправки клиенту при новом NodeError (узел: внутренний/ошибки: 371:5) в ServerResponse.setHeader (узел:_http_outgoing:576:11) в ServerResponse._res.setHeader (F:\javascript\ethiostar\node_modules\next\dist\server\base-server.js:129:24) в sendJson (F:\javascript\ethiostar\node_modules\next\dist\server\api-utils\node.js:195:9) в ServerResponse.apiRes.json (F:\javascript\ethiostar\node_modules\next\dist\server\api-utils\node.js:351:31) в обработчике (webpack-internal:///(api)/./pages/api/submit.js:45:21) при запускеМикрозадачи () в processTicksAndRejections (узел: внутренний/процесс/task_queues:96:5) в асинхронном Object.apiResolver (F:\javascript\ethiostar\node_modules\next\dist\server\api-utils\node.js:367:9) в асинхронном DevServer.runApi (F:\javascript\ethiostar\node_modules\next\dist\server\next-server.js:474:9)
в асинхронном Object.fn (F:\javascript\ethiostar\node_modules\next\dist\server\next-server.js:736:37) в асинхронном Router.execute (F:\javascript\ethiostar\node_modules\next\dist\server\router.js:252:36) в асинхронном DevServer.run (F:\javascript\ethiostar\node_modules\next\dist\server\base-server.js:384:29)
в асинхронном DevServer.run (F:\javascript\ethiostar\node_modules\next\dist\server\dev\next-dev-server.js:732:20) в асинхронном DevServer.handleRequest (F:\javascript\ethiostar\node_modules\ следующий\расстояние\сервер\базовый-сервер.js:322:20) { код: 'ERR_HTTP_HEADERS_SENT', страница: '/api/отправить' }
Вот мой компонент обратной связи в следующем js:
'use client';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
export default function Feedback() {
const { register, handleSubmit, reset } = useForm();
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [alertMessage, setAlertMessage] = useState('');
async function submitHandler(data) {
setLoading(true);
setError('');
try {
const res = await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
},
});
if (res.ok) {
setAlertMessage('message sent successfully');
reset();
} else {
setError('Failed to submit form.');
}
} catch (e) {
console.error(e);
setError('Failed to submit form.');
} finally {
setLoading(false);
}
}
return (
<div className = "container px-6 py-12 mx-auto">
<div className = "lg:flex lg:items-center lg:-mx-6">
<div className = "lg:w-1/2 lg:mx-6">
<h1 className = "text-2xl font-semibold capitalize text-white lg:text-3xl">
Contact us for <br /> more info
</h1>
<div className = "mt-6 space-y-8 md:mt-8">
<p className = "flex items-start -mx-2">
<svg
xmlns = "http://www.w3.org/2000/svg"
className = "w-6 h-6 mx-2 text-blue-400 "
fill = "none"
viewBox = "0 0 24 24"
stroke = "currentColor"
strokeWidth = {2}
>
<path
strokeLinecap = "round"
strokeLinejoin = "round"
d = "M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
/>
<path
strokeLinecap = "round"
strokeLinejoin = "round"
d = "M15 11a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<span className = "mx-2 text-gray-400 truncate w-72 ">
Cecilia Chapman 711-2880 Nulla St. Mankato Mississippi 96522
</span>
</p>
<p className = "flex items-start -mx-2">
<svg
xmlns = "http://www.w3.org/2000/svg"
className = "w-6 h-6 mx-2 text-blue-400"
fill = "none"
viewBox = "0 0 24 24"
stroke = "currentColor"
strokeWidth = {2}
>
<path
strokeLinecap = "round"
strokeLinejoin = "round"
d = "M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"
/>
</svg>
<span className = "mx-2 truncate w-72 text-gray-400">
(257) 563-7401
</span>
</p>
<p className = "flex items-start -mx-2">
<svg
xmlns = "http://www.w3.org/2000/svg"
className = "w-6 h-6 mx-2 text-blue-400"
fill = "none"
viewBox = "0 0 24 24"
stroke = "currentColor"
strokeWidth = {2}
>
<path
strokeLinecap = "round"
strokeLinejoin = "round"
d = "M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
<span className = "mx-2 truncate w-72 text-gray-400">
[email protected]
</span>
</p>
</div>
<div className = "mt-6 w-80 md:mt-8">
<h3 className = "text-gray-300 ">Follow us</h3>
<div className = "flex mt-4 -mx-1.5 ">
<a
className = "mx-1.5 transition-colors duration-300 transform hover:text-blue-500"
href = "#"
>
<svg
className = "w-10 h-10 fill-current"
viewBox = "0 0 24 24"
fill = "none"
xmlns = "http://www.w3.org/2000/svg"
>
<path d = "M18.6668 6.67334C18.0002 7.00001 17.3468 7.13268 16.6668 7.33334C15.9195 6.49001 14.8115 6.44334 13.7468 6.84201C12.6822 7.24068 11.9848 8.21534 12.0002 9.33334V10C9.83683 10.0553 7.91016 9.07001 6.66683 7.33334C6.66683 7.33334 3.87883 12.2887 9.3335 14.6667C8.0855 15.498 6.84083 16.0587 5.3335 16C7.53883 17.202 9.94216 17.6153 12.0228 17.0113C14.4095 16.318 16.3708 14.5293 17.1235 11.85C17.348 11.0351 17.4595 10.1932 17.4548 9.34801C17.4535 9.18201 18.4615 7.50001 18.6668 6.67268V6.67334Z" />
</svg>
</a>
<a
className = "mx-1.5 hover:text-blue-400 text-gray-400 transition-colors duration-300 transform"
href = "#"
>
<svg
className = "w-8 h-8"
viewBox = "0 0 24 24"
fill = "none"
xmlns = "http://www.w3.org/2000/svg"
>
<path
d = "M15.2 8.80005C16.4731 8.80005 17.694 9.30576 18.5941 10.2059C19.4943 11.1061 20 12.327 20 13.6V19.2H16.8V13.6C16.8 13.1757 16.6315 12.7687 16.3314 12.4687C16.0313 12.1686 15.6244 12 15.2 12C14.7757 12 14.3687 12.1686 14.0687 12.4687C13.7686 12.7687 13.6 13.1757 13.6 13.6V19.2H10.4V13.6C10.4 12.327 10.9057 11.1061 11.8059 10.2059C12.7061 9.30576 13.927 8.80005 15.2 8.80005Z"
fill = "currentColor"
/>
<path
d = "M7.2 9.6001H4V19.2001H7.2V9.6001Z"
fill = "currentColor"
/>
<path
d = "M5.6 7.2C6.48366 7.2 7.2 6.48366 7.2 5.6C7.2 4.71634 6.48366 4 5.6 4C4.71634 4 4 4.71634 4 5.6C4 6.48366 4.71634 7.2 5.6 7.2Z"
fill = "currentColor"
/>
</svg>
</a>
<a
className = "mx-1.5 hover:text-blue-400 text-gray-400 transition-colors duration-300 transform"
href = "#"
>
<svg
className = "w-8 h-8"
viewBox = "0 0 24 24"
fill = "none"
xmlns = "http://www.w3.org/2000/svg"
>
<path
d = "M7 10.2222V13.7778H9.66667V20H13.2222V13.7778H15.8889L16.7778 10.2222H13.2222V8.44444C13.2222 8.2087 13.3159 7.9826 13.4826 7.81591C13.6493 7.64921 13.8754 7.55556 14.1111 7.55556H16.7778V4H14.1111C12.9324 4 11.8019 4.46825 10.9684 5.30175C10.1349 6.13524 9.66667 7.2657 9.66667 8.44444V10.2222H7Z"
fill = "currentColor"
/>
</svg>
</a>
<a
className = "mx-1.5 hover:text-blue-400 text-gray-400 transition-colors duration-300 transform"
href = "#"
>
<svg
className = "w-8 h-8"
viewBox = "0 0 24 24"
fill = "none"
xmlns = "http://www.w3.org/2000/svg"
>
<path
d = "M11.9294 7.72275C9.65868 7.72275 7.82715 9.55428 7.82715 11.825C7.82715 14.0956 9.65868 15.9271 11.9294 15.9271C14.2 15.9271 16.0316 14.0956 16.0316 11.825C16.0316 9.55428 14.2 7.72275 11.9294 7.72275ZM11.9294 14.4919C10.462 14.4919 9.26239 13.2959 9.26239 11.825C9.26239 10.354 10.4584 9.15799 11.9294 9.15799C13.4003 9.15799 14.5963 10.354 14.5963 11.825C14.5963 13.2959 13.3967 14.4919 11.9294 14.4919ZM17.1562 7.55495C17.1562 8.08692 16.7277 8.51178 16.1994 8.51178C15.6674 8.51178 15.2425 8.08335 15.2425 7.55495C15.2425 7.02656 15.671 6.59813 16.1994 6.59813C16.7277 6.59813 17.1562 7.02656 17.1562 7.55495ZM19.8731 8.52606C19.8124 7.24434 19.5197 6.10901 18.5807 5.17361C17.6453 4.23821 16.51 3.94545 15.2282 3.88118C13.9073 3.80621 9.94787 3.80621 8.62689 3.88118C7.34874 3.94188 6.21341 4.23464 5.27444 5.17004C4.33547 6.10544 4.04628 7.24077 3.98201 8.52249C3.90704 9.84347 3.90704 13.8029 3.98201 15.1238C4.04271 16.4056 4.33547 17.5409 5.27444 18.4763C6.21341 19.4117 7.34517 19.7045 8.62689 19.7687C9.94787 19.8437 13.9073 19.8437 15.2282 19.7687C16.51 19.708 17.6453 19.4153 18.5807 18.4763C19.5161 17.5409 19.8089 16.4056 19.8731 15.1238C19.9481 13.8029 19.9481 9.84704 19.8731 8.52606ZM18.1665 16.5412C17.8881 17.241 17.349 17.7801 16.6456 18.0621C15.5924 18.4799 13.0932 18.3835 11.9294 18.3835C10.7655 18.3835 8.26272 18.4763 7.21307 18.0621C6.51331 17.7837 5.9742 17.2446 5.69215 16.5412C5.27444 15.488 5.37083 12.9888 5.37083 11.825C5.37083 10.6611 5.27801 8.15832 5.69215 7.10867C5.97063 6.40891 6.50974 5.8698 7.21307 5.58775C8.26629 5.17004 10.7655 5.26643 11.9294 5.26643C13.0932 5.26643 15.596 5.17361 16.6456 5.58775C17.3454 5.86623 17.8845 6.40534 18.1665 7.10867C18.5843 8.16189 18.4879 10.6611 18.4879 11.825C18.4879 12.9888 18.5843 15.4916 18.1665 16.5412Z"
fill = "currentColor"
/>
</svg>
</a>
</div>
</div>
</div>
<div className = "mt-8 lg:w-1/2 lg:mx-6">
<div className = "w-full px-8 py-10 mx-auto overflow-hidden rounded-lg shadow-2xl bg-[#B3FF17] lg:max-w-xl shadow-black/50">
{alertMessage && (
<div className = "flex bg-green-lighter max-w-sm mb-4">
<div className = "w-16 bg-green">
<div className = "p-4">
<svg className = "h-8 w-8 text-white fill-current" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 512 512"><path d = "M468.907 214.604c-11.423 0-20.682 9.26-20.682 20.682v20.831c-.031 54.338-21.221 105.412-59.666 143.812-38.417 38.372-89.467 59.5-143.761 59.5h-.12C132.506 459.365 41.3 368.056 41.364 255.883c.031-54.337 21.221-105.411 59.667-143.813 38.417-38.372 89.468-59.5 143.761-59.5h.12c28.672.016 56.49 5.942 82.68 17.611 10.436 4.65 22.659-.041 27.309-10.474 4.648-10.433-.04-22.659-10.474-27.309-31.516-14.043-64.989-21.173-99.492-21.192h-.144c-65.329 0-126.767 25.428-172.993 71.6C25.536 129.014.038 190.473 0 255.861c-.037 65.386 25.389 126.874 71.599 173.136 46.21 46.262 107.668 71.76 173.055 71.798h.144c65.329 0 126.767-25.427 172.993-71.6 46.262-46.209 71.76-107.668 71.798-173.066v-20.842c0-11.423-9.259-20.683-20.682-20.683z" /><path d = "M505.942 39.803c-8.077-8.076-21.172-8.076-29.249 0L244.794 271.701l-52.609-52.609c-8.076-8.077-21.172-8.077-29.248 0-8.077 8.077-8.077 21.172 0 29.249l67.234 67.234a20.616 20.616 0 0 0 14.625 6.058 20.618 20.618 0 0 0 14.625-6.058L505.942 69.052c8.077-8.077 8.077-21.172 0-29.249z" /></svg>
</div>
</div>
<div className = "w-auto text-grey-darker items-center p-4">
<span className = "text-lg font-bold pb-4">
Thank you!
</span>
<p className = "leading-tight">
{alertMessage}
</p>
</div>
</div>
)}
<h1 className = "text-lg font-medium text-gray-900">
Contact Form
</h1>
<form
onSubmit = {handleSubmit(submitHandler)}
className = "mt-6"
action = "/api/submit"
method = "POST"
>
<div className = "flex-1">
<label className = "block mb-2 text-sm text-gray-900">
Full Name
</label>
<input
type = "text"
name = "name"
placeholder = "Your Name"
autoComplete = "off"
{...register('Name', { required: 'Please Enter Name' })}
className = "block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md dark:placeholder-gray-600 dark:bg-gray-900 dark:text-gray-300 dark:border-gray-700 focus:border-blue-400 dark:focus:border-blue-400 focus:ring-blue-400 focus:outline-none focus:ring focus:ring-opacity-40"
/>
{error && <p className = "text-red">{error}</p>}
</div>
<div className = "flex-1 mt-6">
<label className = "block mb-2 text-sm text-gray-900">
Email address
</label>
<input
type = "email"
name = "email"
placeholder = "Email"
autoComplete = "off"
{...register('Email', { required: 'Please enter valid email' })}
className = "block w-full px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md dark:placeholder-gray-600 dark:bg-gray-900 dark:text-gray-300 dark:border-gray-700 focus:border-blue-400 dark:focus:border-blue-400 focus:ring-blue-400 focus:outline-none focus:ring focus:ring-opacity-40"
/>
{error && <p className = "text-red">{error}</p>}
</div>
<div className = "w-full mt-6">
<label className = "block mb-2 text-sm text-gray-900">
Message
</label>
<textarea
className = "block w-full h-32 px-5 py-3 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-md md:h-48 dark:placeholder-gray-600 dark:bg-gray-900 dark:text-gray-300 dark:border-gray-700 focus:border-blue-400 dark:focus:border-blue-400 focus:ring-blue-400 focus:outline-none focus:ring focus:ring-opacity-40"
name = "message"
placeholder = "Message"
autoComplete = "off"
{...register('Message', { required: 'Please enter message details' })}
/>
{error && <p className = "text-red">{error}</p>}
</div>
<button type = "submit" disabled = {loading} className = "w-full px-6 py-3 mt-6 text-sm font-medium tracking-wide text-white capitalize transition-colors duration-300 transform bg-skin-button-accent hover:bg-skin-button-accent-hover rounded-md focus:outline-none focus:ring focus:ring-blue-300 focus:ring-opacity-50">
Send Message
</button>
</form>
</div>
</div>
</div>
</div>
);
}
вот моя логика submit.js:
import { google } from 'googleapis';
export default async function handler(req, res) {
if (req.method === 'POST') {
const { Name, Email, Message } = req.body;
console.info(Name, Email, Message);
const auth = new google.auth.GoogleAuth({
credentials: {
client_email: process.env.GOOGLE_CLIENT_EMAIL,
private_key: process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
},
scopes: [
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/spreadsheets',
],
});
const sheets = google.sheets({
auth,
version: 'v4',
});
const response = await sheets.spreadsheets.values.append({
spreadsheetId: process.env.GOOGLE_SHEET_ID,
range: 'A1:C1',
valueInputOption: 'USER_ENTERED',
requestBody: {
values: [[Name, Email, Message]],
},
});
res.status(201).json({ message: 'Date entered successfully' });
}
res.status(200).json({ message: 'Done!' });
}
может ли кто-нибудь помочь мне разобраться в проблеме? Я пробовал чат GPT, но он не работает.
Вместо res.status(...) попробуйте выполнить return res.status(....); Вы пытаетесь отправить ответ дважды, поэтому эта ошибка. Но в вашем коде вы можете добавить второй res.status(...) внутри блока else, и это исправит его, даже не нужно возвращать.
Редактировать: отредактировал res.send в res.status
В вашем файле submit.js. Добавьте return перед res.status(201).json({.....}). Или вы можете добавить последний res.status... внутри блока else.
Я исправил проблему, я просто изменил файл submit.js следующим образом:
/* eslint-disable no-unused-vars */
import { google } from 'googleapis';
export default async function handler(req, res) {
if (req.method === 'POST') {
const { Name, Email, Message } = req.body;
console.info(Name, Email, Message);
const auth = new google.auth.GoogleAuth({
credentials: {
client_email: process.env.GOOGLE_CLIENT_EMAIL,
private_key: process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
},
scopes: [
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/spreadsheets',
],
});
const sheets = google.sheets({
version: 'v4',
auth,
});
const response = await sheets.spreadsheets.values.append({
spreadsheetId: process.env.GOOGLE_SHEET_ID,
range: 'A1:C1',
valueInputOption: 'USER_ENTERED',
resource: {
values: [[Name, Email, Message]],
},
});
res.status(201).json({ message: 'Data entered successfully' });
} else {
res.status(200).json({ message: 'Done!' });
}
}
можно поконкретнее пожалуйста? Я применил всю логику с ютуба. Можете ли вы точно показать мне, где разместить логику? извините, заранее спасибо.