この記事では、以前作成した React フォームに Google reCAPTCHA を導入する手順をご紹介します。reCAPTCHA を追加することで、不正な送信やボットからのアクセスを防ぐことができます。
1. プロジェクト構成
以下は、今回のプロジェクト構成例です。
/
├── index.html # フロントエンドコード
├── backend/
│ └── contact.php # バックエンド処理
2. 必要なライブラリとスクリプト
以下のスクリプトを利用します。
- React, ReactDOM
- React Router DOM (バージョン 5.x)
- Google reCAPTCHA v3
スクリプトは <head>
タグ内で読み込みます。
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/react-router-dom@5.3.4/umd/react-router-dom.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>
3. フロントエンドコード
以下は、Reactフォームのコードです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React お問い合わせフォーム</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/react-router-dom@5.3.4/umd/react-router-dom.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>
</head>
<body class="bg-gray-100 min-h-screen flex items-center justify-center">
<div id="root" class="container mx-auto p-4 max-w-screen-md"></div>
<script type="text/babel">
const { BrowserRouter, Route, Switch, useHistory } = window.ReactRouterDOM;
const { useState } = React;
function InputForm() {
const history = useHistory();
const [formData, setFormData] = useState(JSON.parse(localStorage.getItem("formData")) || {
name: "",
email: "",
message: "",
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
};
const handleSubmit = (e) => {
e.preventDefault();
localStorage.setItem("formData", JSON.stringify(formData));
history.push("/confirm");
};
return (
<div className="bg-white shadow-md rounded p-6 w-full max-w-md mx-auto">
<h1 className="text-2xl font-bold mb-4">お問い合わせフォーム</h1>
<form onSubmit={handleSubmit}>
<div className="mb-4">
<label htmlFor="name" className="block text-sm font-medium text-gray-700">名前:</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
required
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
/>
</div>
<div className="mb-4">
<label htmlFor="email" className="block text-sm font-medium text-gray-700">メールアドレス:</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
required
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
/>
</div>
<div className="mb-4">
<label htmlFor="message" className="block text-sm font-medium text-gray-700">お問い合わせ内容:</label>
<textarea
id="message"
name="message"
value={formData.message}
onChange={handleChange}
required
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
/>
</div>
<button type="submit" className="w-full bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700">
確認画面へ
</button>
</form>
</div>
);
}
function ConfirmForm() {
const history = useHistory();
const formData = JSON.parse(localStorage.getItem("formData")) || { name: "", email: "", message: "" };
const handleBack = () => {
history.push("/");
};
const handleSubmit = async () => {
grecaptcha.ready(async () => {
const token = await grecaptcha.execute("YOUR_SITE_KEY", { action: "submit" });
fetch("backend/contact.php", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ...formData, recaptcha_token: token }),
})
.then((res) => res.json())
.then((data) => {
if (data.success) {
localStorage.removeItem("formData");
history.push("/complete");
} else {
alert("送信に失敗しました: " + data.error);
}
})
.catch(() => alert("エラーが発生しました。通信環境を確認してください。"));
});
};
return (
<div className="bg-white shadow-md rounded p-6 w-full max-w-md mx-auto">
<h1 className="text-2xl font-bold mb-4">内容の確認</h1>
<p className="mb-2"><strong>名前:</strong> {formData.name}</p>
<p className="mb-2"><strong>メールアドレス:</strong> {formData.email}</p>
<p className="mb-4"><strong>お問い合わせ内容:</strong> {formData.message}</p>
<div className="flex space-x-4">
<button onClick={handleBack} className="w-1/2 bg-gray-600 text-white py-2 px-4 rounded hover:bg-gray-700">
戻る
</button>
<button onClick={handleSubmit} className="w-1/2 bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700">
送信する
</button>
</div>
</div>
);
}
function CompleteForm() {
const history = useHistory();
return (
<div className="bg-white shadow-md rounded p-6 w-full max-w-md mx-auto">
<h1 className="text-2xl font-bold mb-4">送信が完了しました</h1>
<p>お問い合わせいただきありがとうございます。</p>
<button
onClick={() => history.push("/")}
className="mt-4 w-full bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700"
>
フォームに戻る
</button>
</div>
);
}
function App() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={InputForm} />
<Route path="/confirm" component={ConfirmForm} />
<Route path="/complete" component={CompleteForm} />
</Switch>
</BrowserRouter>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
</script>
</body>
</html>
4. バックエンドコード
以下は、reCAPTCHA の検証を含む PHP コードです。
<?php
header('Content-Type: application/json');
// リクエストデータの取得
$data = json_decode(file_get_contents('php://input'), true);
// reCAPTCHA 検証
$recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify';
$recaptcha_secret = 'YOUR_SECRET_KEY';
$recaptcha_response = $data['recaptcha_token'];
$response = file_get_contents($recaptcha_url . '?secret=' . $recaptcha_secret . '&response=' . $recaptcha_response);
$recaptcha = json_decode($response, true);
if (!$recaptcha['success']) {
echo json_encode(['success' => false, 'error' => 'reCAPTCHA 検証に失敗しました。']);
exit;
}
// 管理者への通知メール
$toAdmin = 'admin@example.com';
$subjectAdmin = 'お問い合わせフォームからのメッセージ';
$messageAdmin = "名前: {$data['name']}\nメールアドレス: {$data['email']}\nメッセージ: {$data['message']}";
$headersAdmin = 'From: noreply@example.com';
$mailToAdmin = mail($toAdmin, $subjectAdmin, $messageAdmin, $headersAdmin);
// 問い合わせ者への自動返信メール
$toUser = $data['email'];
$subjectUser = 'お問い合わせありがとうございます';
$messageUser = "{$data['name']} 様\n\nお問い合わせありがとうございます。\n以下の内容で受け付けました。\n\n----------------------\nお名前: {$data['name']}\nメールアドレス: {$data['email']}\nお問い合わせ内容:\n{$data['message']}\n----------------------\n\n担当者より折り返しご連絡いたします。\n\nよろしくお願いいたします。\n";
$headersUser = 'From: noreply@example.com';
$mailToUser = mail($toUser, $subjectUser, $messageUser, $headersUser);
if ($mailToAdmin && $mailToUser) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'error' => 'メール送信に失敗しました。']);
}
5. 注意点
- YOUR_SITE_KEY と YOUR_SECRET_KEY を Google reCAPTCHA で発行したキーに置き換えてください。
- バックエンドのコードは適切に保護してください(https の利用、サーバーのセキュリティ設定など)。
コメント