ロゴWeb開発ブログ

React + Django REST framework + Google reCAPTCHA V3を使ってお問い合わせを送信する

作成
  • 使用したバージョン
  • react-google-recaptcha-v3 1.10.1
  • drf-recaptcha 3.0.0

以前、reCAPTCHAを使わずにお問い合わせフォームを作ったらボットから大量の迷惑メールが来てしまったことがあった。reCAPTCHAを利用したところ迷惑メールはぱったりと止んだ。

なので、お問い合わせフォームを作るときは必ずreCAPTCHAを使うことにしている。

当サイトのお問い合わせページに行くと右下に謎のマークが出てくるが、これはGoogle reCAPTCHAが有効になっていることを意味している。
お問い合わせページ

今回はこのページで作成したお問い合わせフォームからreCAPTCHAを含めたデータをサーバーに送信させていく。
React(Next.js) + Material UI + React Hook Formでお問い合わせフォームを作る

Google reCAPTCHAへの登録

まず、Google reCAPTCHAにサインアップしたあと、自分のサイトを登録してシークレットキーを取得しておく必要がある。
Google reCAPTCHA

React(Next.js)での実装

ReactでGoogle reCAPTCHAを利用する場合、react-google-recaptcha-v3を使うと簡単に実装できる。


npm install react-google-recaptcha-v3

データの送信処理

お問い合わせフォーム作成ページで作ったuseContactFormを少し変更する。
sendDataはフォームの送信ボタンを押したら実行される関数。


export const useContactForm = () => {
...
const { executeRecaptcha } = useGoogleReCaptcha();
const sendData = async (data: FormValuesType) => {
setIsLoading(true);
try {
if (!executeRecaptcha) throw new Error();
const token = await executeRecaptcha('contact');
const bodyData = {
...data,
recaptcha_token: token
};
const response = await fetch('データを送るURL', {
method: "POST",
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(bodyData)
});
if (!response.ok) throw new Error();
router.push('/success');
} catch {
setHasError(true);
}
setIsLoading(false);
};
return { hasError, isLoading, sendData, closeError };
};

executeRecaptchaを実行してトークンを取得している。
引数にはアクション名を指定する。
ここではcontactとしたが他の文字でもかまわない。
取得したトークンとフォームの入力データをJSON文字列に変換してサーバーに送信している。
もしトークンが無効なものだったらresponse.okfalseとなるためエラーを発生させる。

プロバイダーの作成


'use client'; //Next.jsの場合は必要
import { PropsWithChildren } from 'react';
import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3';
export function ReCaptchaProvider(props: PropsWithChildren) {
return (
<GoogleReCaptchaProvider reCaptchaKey="シークレットキー" scriptProps={{ async: true }}>
{props.children}
</GoogleReCaptchaProvider >
);
};

reCaptchaKeyにサイト登録時に取得したシークレットキーを指定する。
本当はシークレットキーは.envファイルに書いてprocess.env.SECRET_KEYなどのようにして取得するほうがいいけど、ここでは簡略的にベタ書きとした。

このプロバイダーをお問い合わせフォームのコンポーネントよりも上に設置する必要がある。


export default function Page() {
return (
<ReCaptchaProvider>
// お問い合わせフォームのコンポーネント
<Contact />
</ReCaptchaProvider>
);
}

Django側の実装

Django REST frameworkを使っている場合、drf-recaptchaライブラリを使えば簡単に実装することができる。


pip install drf-recaptcha

設定ファイルのsettings.pyにdrf_recaptchaを追加しシークレットキーを書く。


INSTALLED_APPS = [
...,
"drf_recaptcha",
...,
]
...
DRF_RECAPTCHA_SECRET_KEY = "シークレットキー"

シリアライザーの作成


from rest_framework import serializers
from drf_recaptcha.fields import ReCaptchaV3Field
class ContactSerializer(serializers.Serializer):
name = serializers.CharField(allow_blank=True, default='匿名')
email = serializers.EmailField()
message = serializers.CharField()
recaptcha_token = ReCaptchaV3Field(action='contact')

ReCaptchaV3Field以外のフィールドは自由に設定してOK。
ReCaptchaV3Fieldactionにはクライアント側で指定したアクション名を渡す。

ビューの作成

ここではAPIViewを継承した単純なビューを作成している。
POSTのリクエストが来るとpostメソッドが実行される。


class ListUsers(APIView):
def post(self, request, format=None):
serializer = ContactSerializer(data=request.data, context={"request": request})
serializer.is_valid(raise_exception=True)
email = serializer.data['email']
name = serializer.data['name']
message = serializer.data['message']
# 正常なリクエストだった場合の処理を書く
# 自分のメールアドレスに送るなど
...
return Response()

シリアライザーのインスタンスを生成したのちserializer.is_valid(raise_exception=True)で送られてきたデータを検査している。
raise_exception=Trueにすることで不正なトークンだったりするとクライアント側にエラーレスポンスが返されることになる。
正常なリクエストだった場合、メールを送るなどの処理をしたのち、クライアント側にステータス200のレスポンスが返される。