以前、reCAPTCHAを使わずにお問い合わせフォームを作ったらボットから大量の迷惑メールが来てしまったことがあった。reCAPTCHAを利用したところ迷惑メールはぱったりと止んだ。
なので、お問い合わせフォームを作るときは必ずreCAPTCHAを使うことにしている。
当サイトのお問い合わせページに行くと右下に謎のマークが出てくるが、これはGoogle reCAPTCHAが有効になっていることを意味している。
お問い合わせページ
今回はこのページで作成したお問い合わせフォームからreCAPTCHAを含めたデータをサーバーに送信させていく。
React(Next.js) + Material UI + React Hook Formでお問い合わせフォームを作る
まず、Google reCAPTCHAにサインアップしたあと、自分のサイトを登録してシークレットキーを取得しておく必要がある。
Google reCAPTCHA
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.ok
はfalse
となるためエラーを発生させる。
'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 REST frameworkを使っている場合、drf-recaptchaライブラリを使えば簡単に実装することができる。
pip install drf-recaptcha
設定ファイルのsettings.pyにdrf_recaptchaを追加しシークレットキーを書く。
INSTALLED_APPS = [ ..., "drf_recaptcha", ...,]...DRF_RECAPTCHA_SECRET_KEY = "シークレットキー"
from rest_framework import serializersfrom drf_recaptcha.fields import ReCaptchaV3Fieldclass ContactSerializer(serializers.Serializer): name = serializers.CharField(allow_blank=True, default='匿名') email = serializers.EmailField() message = serializers.CharField() recaptcha_token = ReCaptchaV3Field(action='contact')
ReCaptchaV3Field
以外のフィールドは自由に設定してOK。
ReCaptchaV3Field
のaction
にはクライアント側で指定したアクション名を渡す。
ここでは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のレスポンスが返される。