编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

让我们用 Next.js 构建一个联系表单

wxchong 2024-07-22 22:43:07 开源技术 13 ℃ 0 评论

大多数网站都有一个联系页面,您可以在其中发送消息以联系所有者。 它们看起来像这样:

在本文中,我们将在 Next.js 中使用 React 创建一个类似的表单。 首先,我将创建一个带有表单的前端部分,然后我将构建一个 API 路由,将表单发送到您的电子邮件。

设置应用程序

首先,让我们创建一个新的 Next.js 项目。 我们将在启用 JavaScript 和 ESLint 的 contact-form 文件夹中创建它:

npx create-next-app contact-form --js --eslint

这将创建文件夹并安装所有依赖项。

现在进入文件夹(cd contact-form)并启动开发服务器:

npm run dev

访问 http://localhost:3000 查看正在运行的应用程序。

创建表单

我们要进行更改的主要文件是 pages/index.js。 删除文件中的原始代码并粘贴以下代码:

import React from 'react';

export default function Home() {
  return (
    <form className="container">
      <h1>Get in touch</h1>
      <div className="email block">
        <label htmlFor="frm-email">Email</label>
        <input
          id="frm-email"
          type="email"
          name="email"
          autoComplete="email"
          required
        />
      </div>
      <div className="block phone">
        <label htmlFor="frm-phone">Phone</label>
        <input
          id="frm-phone"
          type="text"
          name="phone"
          autoComplete="tel"
          required
        />
      </div>
      <div className="name block">
        <div>
          <label htmlFor="frm-first">First Name</label>
          <input
            id="frm-first"
            type="text"
            name="first"
            autoComplete="given-name"
            required
          />
        </div>
        <div>
          <label htmlFor="frm-last">Last Name</label>
          <input
            id="frm-last"
            type="text"
            name="last"
            autoComplete="family-name"
            required
          />
        </div>
      </div>
      <div className="message block">
        <label htmlFor="frm-message">Message</label>
        <textarea id="frm-message" rows="6" name="message"></textarea>
      </div>
      <div className="button block">
        <button type="submit">Submit</button>
      </div>
    </form>
  );
}

此代码创建一个包含以下字段的表单:

电子邮件

电话号码

信息

除消息外,所有字段都是必需的。

要向表单添加样式,请将 styles/globals.css 文件的内容替换为以下代码:

html,
body {
  padding: 0;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu,
    Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  background: #1e1e1e;
  min-height: 100vh;
  display: flex;
  color: rgb(243, 241, 239);
  justify-content: center;
  align-items: center;
}

.block {
  display: flex;
  flex-direction: column;
}

.name {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
}

.container {
  font-size: 1.3rem;
  border-radius: 10px;
  width: 85%;
  padding: 50px;
  box-shadow: 0 54px 55px rgb(78 78 78 / 25%), 0 -12px 30px rgb(78 78 78 / 25%),
    0 4px 6px rgb(78 78 78 / 25%), 0 12px 13px rgb(78 78 78 / 25%),
    0 -3px 5px rgb(78 78 78 / 25%);
}

.container input {
  font-size: 1.2rem;
  margin: 10px 0 10px 0px;
  border-color: rgb(31, 28, 28);
  padding: 10px;
  border-radius: 5px;
  background-color: #e8f0fe;
}

.container textarea {
  margin: 10px 0 10px 0px;
  padding: 5px;
  border-color: rgb(31, 28, 28);
  border-radius: 5px;
  background-color: #e8f0fe;
  font-size: 20px;
}

.container h1 {
  text-align: center;
  font-weight: 600;
}

.name div {
  display: flex;
  flex-direction: column;
}

.block button {
  padding: 10px;
  font-size: 20px;
  width: 30%;
  border: 3px solid black;
  border-radius: 5px;
}

.button {
  display: flex;
  align-items: center;
}

textarea {
  resize: none;
}

我们的表单应该看起来像这样:

现在我们需要找到一种方法来存储用户输入的输入。 我将使用当前所有浏览器都原生支持的 FormData。 它从表单加载字段,然后将它们提交给服务器。

在 pages/index.js 文件中,粘贴以下代码(注意新的 handleSubmit 函数):

import React from 'react';

export default function Home() {
  async function handleSubmit(e) {
    e.preventDefault();
    const data = new FormData(e.currentTarget);
    console.log(data);
  }

  return (
    <form className="container" onSubmit={handleSubmit}>
      <h1>Get in touch</h1>
      <div className="email block">
        <label htmlFor="frm-email">Email</label>
        <input
          id="frm-email"
          type="email"
          name="email"
          autoComplete="email"
          required
        />
      </div>
      <div className="block phone">
        <label htmlFor="frm-phone">Phone</label>
        <input
          id="frm-phone"
          type="text"
          name="phone"
          autoComplete="tel"
          required
        />
      </div>
      <div className="name block">
        <div>
          <label htmlFor="frm-first">First Name</label>
          <input
            id="frm-first"
            type="text"
            name="first"
            autoComplete="given-name"
            required
          />
        </div>
        <div>
          <label htmlFor="frm-last">Last Name</label>
          <input
            id="frm-last"
            type="text"
            name="last"
            autoComplete="family-name"
            required
          />
        </div>
      </div>
      <div className="message block">
        <label htmlFor="frm-message">Message</label>
        <textarea id="frm-message" rows="6" name="message"></textarea>
      </div>
      <div className="button block">
        <button type="submit">Submit</button>
      </div>
    </form>
  );
}

现在,当您尝试提交表单时,您应该会在开发人员控制台中看到 FormData。

向 API 提交表单

我们将使用 [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) 将表单数据提交给 API——不需要额外的依赖。

表单提交逻辑转到 handleSubmit 函数。 以下是 pages/index.js 中的完整代码:

import React from 'react';

export default function Home() {
  async function handleSubmit(e) {
    e.preventDefault();
    const data = new FormData(e.currentTarget);
    try {
      const response = await fetch('/api/contact', {
        method: 'post',
        body: new URLSearchParams(data),
      });
      if (!response.ok) {
        throw new Error(`Invalid response: ${response.status}`);
      }
      alert('Thanks for contacting us, we will get back to you soon!');
    } catch (err) {
      console.error(err);
      alert("We can't submit the form, try again later?");
    }
  }

  return (
    <form className="container" onSubmit={handleSubmit}>
      <h1>Get in touch</h1>
      <div className="email block">
        <label htmlFor="frm-email">Email</label>
        <input
          id="frm-email"
          type="email"
          name="email"
          autoComplete="email"
          required
        />
      </div>
      <div className="block phone">
        <label htmlFor="frm-phone">Phone</label>
        <input
          id="frm-phone"
          type="tel"
          name="phone"
          autoComplete="tel"
          required
        />
      </div>
      <div className="name block">
        <div>
          <label htmlFor="frm-first">First Name</label>
          <input
            id="frm-first"
            type="text"
            name="first"
            autoComplete="given-name"
            required
          />
        </div>
        <div>
          <label htmlFor="frm-last">Last Name</label>
          <input
            id="frm-last"
            type="text"
            name="last"
            autoComplete="family-name"
            required
          />
        </div>
      </div>
      <div className="message block">
        <label htmlFor="frm-message">Message</label>
        <textarea id="frm-message" rows="6" name="message"></textarea>
      </div>
      <div className="button block">
        <button type="submit">Submit</button>
      </div>
    </form>
  );
}

handleSubmit 将 POST 请求中的数据发送到 /api/contact 路由。 我们还将 FormData 对象包装在 URLSearchParams 中,以将数据作为 application/x-www-form-urlencoded 发送,该数据由 Next.js API 路由处理程序自动解码。

在 API 路由中处理表单提交

现在我们需要处理服务器上的表单提交。 我们将为此使用 Next.js API 路由。 API 路由位于 pages/api 文件夹中。 让我们创建对应于 API 路由 /api/contact 的 pages/api/contact.js 文件。

首先在 pages/api/contact.js 文件中粘贴以下代码来测试我们是否在服务器上接收到数据。

export default function handler(req, res) {
  console.log(req.body);
  res.send(200);
}

现在尝试提交表单,您应该会看到终端上记录的数据。 现在我们进入了有趣的部分。

使用 SendGrid 和 Superface 发送电子邮件

当用户提交联系表时,我们希望将提交的信息发送给网站所有者。 首先,我们需要选择一些电子邮件提供商并研究他们的 API 和 SDK。 或者我们可以将 Superface 与任何提供商一起使用。

Superface 使 API 集成变得超级简单。 我们不必处理 API 文档,我可以在同一接口后面使用许多提供程序。 此外,我可以使用 Superface 目录中更多现成的 API 用例。 这是一个值得在您的工具箱中拥有的工具。

设置发送网格

我将使用 SendGrid 作为 Superface 的电子邮件提供商。 创建您的帐户,获取具有完全访问权限的 API 密钥并验证单一发件人验证。

在 Superface 端,选择用例,即:发送电子邮件。

从 API 路由发送电子邮件

Superface 用例与 OneSDK 一起使用,因此我们必须安装它。

npm i @superfaceai/one-sdk

在发送电子邮件用例的 Superface 上,选择 sendgrid 作为提供者。 我们可以在 API 路由处理程序中使用示例中的大部分代码,我们只需要从请求中正确传递数据即可。

将以下代码粘贴到您的 pages/api/contact.js 文件中:

const { SuperfaceClient } = require('@superfaceai/one-sdk');

const sdk = new SuperfaceClient();

//只需检查是否提供了所有必填字段
function formValid(body) {
  return body.email && body.phone && body.first && body.last;
}

export default async function handler(req, res) {
  const body = req.body;

  if (!formValid(body)) {
    res.status(422).end();
    return;
  }

  const profile = await sdk.getProfile('communication/send-email@2.1.0');
  const message = `
    Email: ${body.email}
    Phone: ${body.phone}
    Name: ${body.first} ${body.last}
    Message: ${body.message} 
    `;
  const result = await profile.getUseCase('SendEmail').perform(
    {
      from: process.env.FROM_EMAIL,
      to: process.env.TO_EMAIL,
      subject: 'Message from contact form',
      text: message,
    },
    {
      provider: 'sendgrid',
      security: {
        bearer_token: {
          token: process.env.SENDGRID_API_KEY,
        },
      },
    }
  );

  try {
    const data = result.unwrap();
    console.log(data);
    res.status(201).end();
  } catch (error) {
    console.error(error);
    res.status(500).end();
  }
}

您会注意到我们在代码中引用了一些环境变量(例如 process.env.SENDGRID_TOKEN)。 我们可以将这些存储在 .env 文件中。 在项目的根目录中,创建一个包含以下内容的 .env.local 文件(确保编辑值):

# 在单一发件人验证中验证的电子邮件地址
FROM_EMAIL=from@example.com
# 您要将提交内容发送到的电子邮件地址
TO_EMAIL=to@example.com
# Sendgrid API key
SENDGRID_API_KEY=SG.abcdef...

我们的应用程序已准备就绪。 使用 npm run dev 运行(或重启)开发服务器并尝试提交表单!

结论

我们已经学习了如何在 Next.js 中创建表单,使用 FormData 提交表单并获取,在 API 路由中处理提交并通过电子邮件发送。

进一步可能的改进包括添加 CAPTCHA 或蜜罐字段以防止垃圾邮件提交,通过检查电话和电子邮件地址改进表单数据验证,将提交格式化为 HTML,或在提交时向用户提供更好的反馈。

除了发送电子邮件,我们还可以通过提交做更多的事情,比如将数据发送到 CRM 或 Slack,或者处理时事通讯订阅。 但那是另一次——关注我们的个人资料或注册我们的每月时事通讯,这样您就不会错过我们以后的教程。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表