svelteKit `/typo` 中的提取请求问题 400(错误请求),未捕获(承诺中)语法错误:JSON 输入意外结束

问题描述 投票:0回答:1

我目前正在利用 svelteKit 创建一个网页主页。

我正在创建一个系统,当用户输入

/typo
路径时,他们会填写页面上的表单,当他们按下
Submit
按钮时,它会创建并通过 Googlemail 发送电子邮件。但是,在我的本地环境(开发服务器)中,电子邮件发送正常,但部署后,我收到
400 Bad Request
错误。 你能根据我的代码和截图告诉我问题是什么吗?

我已基于node.js将部署环境设置为

adapter-node
,构建后,我使用
index.js
build
文件夹中运行
pm2

为什么在部署在我的开发环境(localhost)中运行良好的功能后会出现此问题?

我附上了书面代码的图片,错误消息控制台。



//svelte.config.js

import adapter from '@sveltejs/adapter-node';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter({
      host: '0.0.0.0',
      port: 5173,
    }),
  }
};

export default config;
// src/hooks.js

export async function handle({ request, resolve }) {
  const response = await resolve(request);
  response.headers.set('Access-Control-Allow-Origin', '*');
  response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  response.headers.set('Access-Control-Allow-Headers', '*');
  response.headers.set('Access-Control-Allow-Credentials', 'true');

  return response;
}
// src/lib/server/sendEmail.js
import nodemailer from "nodemailer";
import { GMAIL_ADDRESS, GMAIL_PASS } from "$env/static/private";

const transporter = nodemailer.createTransport({
  service: "gmail",
  auth: {
    user: GMAIL_ADDRESS,
    pass: GMAIL_PASS,
  },
  tls: {
    rejectUnauthorized: false,
  },
});

const sendEmail = async (subject, content) => {
  try {
    const info = await transporter.sendMail({
      from: GMAIL_ADDRESS,
      to: GMAIL_ADDRESS, 
      subject: subject,
      html: content, 
    });
    console.log("Email sent: ", info.messageId);
  } catch (error) {
    console.error("sendEmail error: ", error);
    throw error;
  }
};

export default sendEmail;
// src/routes/typo/+server.js

import upload from '$lib/server/upload.js';
import { json, error } from '@sveltejs/kit';
import sendEmail from '$lib/server/sendEmail.js';
import crypto from 'crypto';
import { CAPTCHA_SALT } from '$env/static/private';

export async function POST({ request, cookies }) {
  const data = await request.formData();
  const captchaText = data.get('captchaText');
  const hash = data.get('hash');

  let userHash = crypto.createHash('md5').update(captchaText + CAPTCHA_SALT).digest('hex');
  if (hash !== userHash) {
    throw error(400, 'Captcha verification failed');
  }

  const name = data.get('name');
  const bookTitle = data.get('bookTitle');
  const email = data.get('email');
  const phone = data.get('phone');
  const inquiry = data.get('inquiry');

  const content =
`<p>이름: ${name}</p>
<p>이메일: ${email}</p>
<p>연락처: ${phone}</p>
<p>책 제목: ${bookTitle}</p>
<p style="white-space: pre-line;">오탈자 내용: ${inquiry}</p>
`

  try {
    await sendEmail(
      `[오타 제보] ${name} 님 | ${bookTitle}`, // 메일 제목
      content, 
      email 
    );
  } catch (err) {
    console.error(err);
    throw error(500, 'Failed to send email');
  }

  return json({ message: 'ok' });
}

<!--src/routes/typo/+page.svelte-->

<script>
  export let data;

  import BookSearch from "../../components/BookSearch.svelte";
    import ContentHeading from "../../components/ContentHeading.svelte";
    import SubmitButton from "../../components/SubmitButton.svelte";
    import TitleSection from "../../components/TitleSection.svelte";

  let captchaText = '';

  let books = data.books;

  let bookId = null;
  let selectedBook = null;
  let name = '';
  let phone = '';
  let email = '';
  let inquiry = '';

  const onSelectBook = (id) => {
    bookId = id;
    selectedBook = books.find(book => book.id === id);
  }

  const submit = async () => {
    if (!confirm('제출하시겠습니까?')) return;

    if (!name) { alert('이름을 입력하세요.'); return; }
    if (!email) { alert('이메일을 입력하세요.'); return; }
    let emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
    if (!emailRegex.test(email)) { alert('이메일 형식이 올바르지 않습니다.'); return; }
    if (!phone) { alert('연락처를 입력하세요.'); return; }
    if (!bookId) { alert('도서를 선택하세요.'); return; }
    if (!captchaText) { alert('자동입력방지 문자를 입력하세요.'); return; }

    const formData = new FormData();

    formData.append('name', name);
    formData.append('email', email);
    formData.append('phone', phone);
    formData.append('bookTitle', selectedBook.attributes.title);
    formData.append('inquiry', inquiry);
    formData.append('captchaText', captchaText);
    formData.append('hash', data.hash);

    const res = await fetch('/typo', {
      method: 'POST',
      body: formData,
    });

    if (res.status === 200) {
      alert('제출되었습니다.');
    } else {
      let errorText = (await res.json()).message;
      alert(`제출에 실패했습니다.\n${errorText}`);
    }
  }

</script>

<TitleSection title="오탈자 제보" />

<div class="about-mission-area pb-70">
  <div class="container">
    <ContentHeading name="오탈자 제보" />
    <div class="row">
      <div class="col-lg-12 col-md-12">
        <div class="container"  style="padding:0px">
          <form>
            <table class="table" >
              <tbody>
                <tr class="table-row">
                  <td class="bg-gray-3 header-text">이름</td>
                  <td><input bind:value={name} type="text" class="form-control" id="name" placeholder="이름을 입력하세요"></td>
                </tr>
                <tr class="table-row">
                  <td class="bg-gray-3 header-text">이메일</td>
                  <td><input bind:value={email} type="email" class="form-control" id="email" placeholder="이메일 주소를 입력하세요"></td>
                </tr>
                <tr class="table-row">
                  <td class="bg-gray-3 header-text">연락처</td>
                  <td><input bind:value={phone} type="tel" class="form-control" id="phone" placeholder="연락처를 입력하세요"></td>
                </tr>
                <tr class="table-row">
                  <td class="bg-gray-3 header-text">도서명</td>
                  <td class="book-search">
                    <div class="book-title">
                      <span>{selectedBook ? selectedBook.attributes.title : "책을 선택해주세요."}</span>
                    </div>
                    <BookSearch books={books} onSelect={onSelectBook}/>
                  </td>
                </tr>
                <tr class="table-row">
                  <td class="bg-gray-3 header-text">오탈자 내용</td>
                  <td><textarea bind:value={inquiry} class="form-control" id="exampleFormControlTextarea1" rows="8"></textarea></td>
                </tr>
              </tbody>
            </table>
            <!--captcha-->
            <div class="d-flex flex-column align-items-center mb-3">
              {@html data.captcha}
              <input bind:value={captchaText} type="text" class="form-control mt-2" id="captcha" placeholder="위의 문자를 입력하세요">
            </div>
            <SubmitButton onClick={submit} text="제출"/>
          </form>
        </div>
      </div>
    </div>
  </div>
</div>

<style>
  .book-search {
    display: flex;
    flex-direction: column;
  }


  #captcha {
    max-width: 260px;
  }

  .table-row {
    padding-bottom: 10px !important;
    border-bottom: 5px solid white !important;
    font-family: 'Nanum Gothic', sans-serif !important;
    font-weight: 700 !important;
  }

  .header-text {
    text-align: left;
    vertical-align: middle !important;
    font-size: 1rem;
    width: 150px;
  }
</style>

本地主机开发环境截图

localhost_ui

localhost_header

localhost_payload

localhost_result


制作截图

production_console

production_header


任何帮助将不胜感激。

  1. 检查是否是在部署的服务器上使用 smtp 的端口转发问题,但一切正常。

  2. 检查是否是环境变量的问题,但是在开发环境下运行正常。

  3. 我尝试直接在请求 URL 中的“/typo”中指定服务器域。 但没有成功。

cors fetch sveltekit 400-bad-request sveltekit-adapter-node
1个回答
0
投票

我尝试在评论中添加尽可能多的细节,但似乎最好写在这里。首先,我不认为这是 SvelteKit 的问题。更像是 PM2 配置问题。

要检查是否不存在,请尝试运行以下命令:

PORT=YOURPORT ORIGIN=http://website.com node index.js 

如果它没有引发任何 CORS 错误,那就明白了!如果不是,它可能确实来自 Sveltekit。

首先停止pm2,并禁用你的应用程序。

pm2 stop index.js && pm2 disable [portId]

在您的应用程序文件夹中添加配置文件。

{
    "apps" : [
        {
            "name": "from",
            "script": "./production/index.js", <- your index.js file location
            "node_args": [
            ],
            "autorestart": false,
            "exec_interpreter": "node",
            "exec_mode": "cluster",
            "instances": 1,
            "env": {
                "NODE_ENV": "development"
            },
            "env_production": {
                "ORIGIN": "https://yourwebsite.com", <- ORIGIN
                "PORT": "PORT" <- Remember ""
            }
        }
    ]
}

(这是我在生产服务器上使用的文件。您可能需要调整一些设置)

启动PM2

pm2 start .server_config.json --env production 

记住

--env production
。检查它是否有效。另外,不要向 Access-Control-Allow-Origin 添加任何包罗万象的内容。任何人都可以滥用您的表单和 Gmail API。

© www.soinside.com 2019 - 2024. All rights reserved.