はじめに
先日、Googleフォームから送信したデータを、DynamoDBに登録する小さいアプリを作ったのですが、こちらをCloudFrontからの配信に変えて、さらにフォームを自作のHTMLページに変更してみたいと思います。
《前回の記事はこちら》
Googleフォームからデータを送信してDynamoDBに登録する
API Gatewayから後ろの部分(API Gateway、Lambda、DynamoDB)には変更を加えませんので、これらのリソースの状態などは前回の記事を参照いただければと思います。
自作のHTMLのフォームページはS3からの静的ウェブサイトホスティングとして、最終的には独自ドメインからの配信としてみたいと思います。
やること
今回やることを一覧化すると、以下のようになります。
- HTMLで作ったフォームページのご紹介(参考サイトに記載した有識者の皆さまありがとうございました)
- HTMLフォームページをS3の静的ウェブサイトホスティングとして設定
- API GatewayのCORSを有効化する
- S3に配置したフォームページからデータを送信してDynamoDBに登録されることを確認する
- CloudFrontを作成してS3をオリジンとして登録する。そしてCloudFront経由でS3の静的ウェブサイトにアクセスしてAPI Gateway経由でDynamoDBに登録できることを確認する
- Route 53に独自ドメインのホストゾーンを作成してネームサーバーをRoute 53に向ける
- ACMで証明書を作成する
- 独自ドメインをCloudFrontドメインに名前解決する設定をRoute 53に入れる
- 独自ドメインでアクセスしてS3静的ウェブサイトから送信したデータがDynamoDBに登録されることを確認する
構成図
変更前
前回の記事で作成した状態です。
変更後
完成後のイメージです。
やってみる
HTMLフォーム
作成したHTMLフォームのコードです。
<!DOCTYPE html>
<html lang="ja">
<head>
<title>問い合わせフォーム(Atsushinotes)</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script language="javascript" type="text/javascript">
function FormSubmit() {
//API GatewayエンドポイントのURL
var api_url = "https://mag7j9olvf.execute-api.ap-northeast-1.amazonaws.com/v1/test";
//フォームから値を受け取る
var target = document.getElementById("test-form");
var resEmail = target.elements[0].value;
var resName = target.elements[1].value;
var resInquiry = target.elements[4].value;
//チェックされたラジオボタンの値を取得する。
var genderList = target.gender;
for (i = 0; i < genderList.length; i++){
if(genderList[i].checked){
//コンソールに値を出力する
resGender = genderList[i].value;
}
}
//API Gatewayに渡すため変数をJSONにまとめる。
var jsonObj = {
"email": resEmail,
"name": resName,
"gender": resGender,
"inquiry": resInquiry
}
//API GatewayにPOSTする。
$.ajax({
type: "POST",
url: api_url,
dataType: "json",
crossDomain: "true",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(jsonObj),
success: function(){
alert("成功!");
document.getElementById("test-form").reset();
location.reload();
},
error: function(){
alert("失敗!")
}
});
}
</script>
</head>
<body>
<h1>問い合わせフォームテスト</h1>
<h2>各項目を入力して[送信]ボタンを押してください</h2>
<form id="test-form" method="post">
<p>メールアドレス</p>
<input type="email" name="email" placeholder="メールアドレスを入力" /><br><br>
<p>名前</p>
<input type="text" name="name" placeholder="名前を入力" /><br><br>
<p>性別</p>
<label><input type="radio" name="gender" value="male"> 男</label>
<label><input type="radio" name="gender" value="female"> 女</label><br><br>
<p>問い合わせ内容</p>
<textarea type="textarea" name="inquiry" placeholder="問い合わせ内容を記載"></textarea><br><br>
<button type="button" onClick="FormSubmit()">送信</button>
</form>
</body>
</html>
メールアドレスは「email」、名前は「text」、性別は「radio」、問い合わせ内容は改行ができるように「textarea」のtypeで作成しました。
「radio」の性別はチェックが入っていたものを確認して取得します。
これらの問い合わせフォームに入れられた値をJSONオブジェクトとしてまとめます。(「jsonObj」変数)
ajaxでAPI Gatewayに向けてPOSTします。
画面は以下のようになっています。
S3に配置して静的ウェブサイトホスティング
S3に「bucket-atsushi0」というバケットを作成して、その中にアップロードしました。
パブリックアクセスブロックは無効にしました。
バケットポリシーは以下のようになっていて、すべてのアクセス元から(”Principal”: “*”)、bucket-atsushi0のバケット内のすべてのオブジェクトに対して(”Resource”: “arn:aws:s3:::bucket-atsushi0/*”)、取得ができる(”Action”: “s3:GetObject”)としています。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::bucket-atsushi0/*"
}
]
}
バケットのプロパティタブから、静的ウェブサイトホスティングを有効化しました。インデックスドキュメントはファイル名の「index.html」です。
上のURLにアクセスすると問い合わせフォームを表示できます。
API GatewayのCORS有効化
CORSの詳細についてはここでは触れません。API GatewayのCORSを有効化するのみです。
API Gatewayにアクセスして、「リソース」から前回の記事で作成したPOSTメソッドを選択し、「アクション」から「CORSの有効化」をクリックします。
デフォルトの設定値で、「CORSを有効にして既存のCORSヘッダーを置換」ボタンをクリックします。
確認画面が出るので「はい、既存の値を置き換えます」をクリックします。
下のように何もエラーが出なければOKです。
前回の記事で作成した「v1」ステージにデプロイします。
余談(なぜGoogleフォームの場合はCORS無効でもよかったのか?)
余談ですが、前回の記事でGoogleフォームから実施した時はCORSが無効化状態でした。Googleの異なるドメインからのアクセスなのにCORSが無効化でも実行できたのは、Googleフォームからのリクエストが単純リクエストであったたためだと思います。
《単純リクエストについて》
https://developer.mozilla.org/ja/docs/Web/HTTP/CORS#%E5%8D%98%E7%B4%94%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88
試しにGoogleフォームから送信したときのリクエストヘッダーを見てみると、単純リクエストの条件を満たしているように見えます。
methodはPOST、acceptやaccept-languageといったヘッダーが使われている。そしてContent-Typeヘッダーはapplication/x-www-form-urlencodedとなっている。
S3ウェブサイトホスティングからのアクセスで動作確認
ここでいったん動作確認します。
先ほどのS3の静的ウェブサイトホスティングのURLにアクセスし、適当な値を入力して送信します。
問い合わせフォームのHTMLファイルのJavaScriptに実装した条件分岐で、成功が表示されることを確認します。
DynamoDBを見てみます。
DBにデータが登録されていますね。ひとまず成功です。
S3ウェブサイトをCloudFrontからの配信にしてみる
超簡単でした。
CloudFrontを作成して、オリジンにバケット(ここでは「bucket-atsushi0」)を指定してあげればよかったです。
URLは「http://ディストリビューションドメイン名/index.html」でアクセスします。
送信してみると、DynamoDBに登録されました。
Route 53にホストゾーン作成とネームサーバー設定
最後は独自ドメインでS3の静的ウェブサイトにアクセスできるようにするのですが、まずRoute 53に独自ドメインのホストゾーンを作成します。
独自ドメインは「atsushinotes.work」でお名前ドットコムで取得しました。
Route 53でホストゾーンを作成すると、以下のようにネームサーバーが払い出されるのでこれらをメモします。
メモしたネームサーバーをお名前ドットコム(レジストラ)の設定画面で、対象のドメイン(ここでは「atsushinotes.work」)に設定します。
反映に24時間〜72時間かかることもあるそうです。その間に後述の手順を実施します。
補足(ドメインのネームサーバーが変わったことの確認)
ターミナルで下記コマンドを実行します。ANSWER SECTIONにRoute 53のネームサーバーが表示されれば変更完了しています。
% dig atsushinotes.work ns
; <<>> DiG 9.10.6 <<>> atsushinotes.work ns
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50629
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 9
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;atsushinotes.work. IN NS
;; ANSWER SECTION:
atsushinotes.work. 172800 IN NS ns-124.awsdns-15.com.
atsushinotes.work. 172800 IN NS ns-1424.awsdns-50.org.
atsushinotes.work. 172800 IN NS ns-1979.awsdns-55.co.uk.
atsushinotes.work. 172800 IN NS ns-984.awsdns-59.net.
;; ADDITIONAL SECTION:
ns-124.awsdns-15.com. 75844 IN A 205.251.192.124
ns-124.awsdns-15.com. 77971 IN AAAA 2600:9000:5300:7c00::1
ns-1424.awsdns-50.org. 74795 IN A 205.251.197.144
ns-1424.awsdns-50.org. 75484 IN AAAA 2600:9000:5305:9000::1
ns-1979.awsdns-55.co.uk. 75082 IN A 205.251.199.187
ns-1979.awsdns-55.co.uk. 77445 IN AAAA 2600:9000:5307:bb00::1
ns-984.awsdns-59.net. 74031 IN A 205.251.195.216
ns-984.awsdns-59.net. 74594 IN AAAA 2600:9000:5303:d800::1
;; Query time: 52 msec
;; SERVER: 192.168.0.254#53(192.168.0.254)
;; WHEN: Thu Mar 03 11:35:46 JST 2022
;; MSG SIZE rcvd: 362
%
ACMで証明書を取得
独自ドメインでアクセスしてCloudFront経由でS3の静的ウェブサイトにアクセスするには、ACM(Amazon Certificate Manager)で証明書を取得する必要があります。
独自ドメインからCloudFront経由でアクセスするために必要なことをまとめてみます。
- ACMで証明書取得。
- Route 53に独自ドメインをCloudFrontドメインに解決するAレコード(エイリアス)を作成
- 証明書をCloudFrontに設定して、CloudFrontの代替ドメイン名に独自ドメインを設定
ACMのコンソールにアクセスし、パブリック証明書をリクエストします。このとき、リージョンは「バージニア北部」(us-east-1)で取得します。
独自ドメイン「atsushinotes.work」にアクセスしたらCloudFrontにリクエストしてほしいので、完全修飾ドメイン名は「atsushinotes.work」にします。
検証方法は今回は「DNS」にしました。Eメールでも問題ありません。ドメインの所有を証明することで証明書が使えるようになります。「DNS」の方法だとRoute 53にCNAMEレコードを追加するだけで証明できます。
リクエストした証明書を選択して、「Route 53でレコード作成」ボタンをクリックします。
「レコードを作成」をクリックします。
Route 53のatsushinotes.workホストゾーンを確認します。ドメイン所有の証明用のCNAMEレコードが作成されています。
ACMのコンソール上でもリクエストした証明書のステータスが「発行済み」になっています。
これでCloudFrontに独自ドメインを設定する準備が整いました。
独自ドメインをCloudFrontドメインに解決する設定
独自ドメインにアクセスしたらCloudFrontにアクセスするように名前解決の設定を入れます。
CloudFrontドメインをコピーします。
Route 53のコンソールで独自ドメインのホストゾーンを開いて、以下のように入力します。
- レコード名:空にします。atsushinotes.workでアクセスしてページを表示したいので、サブドメインは設定しません。
- レコードタイプ:Aレコードを選択します。Aレコードはホスト名をIPアドレスに変換するレコードですが、AWS独自のエイリアス設定を使ってCloudFrontドメインに名前解決します。
- トラフィックのルーティング先:「エイリアス」にチェックを入れ(これ重要)、先ほどコピーしたCloudFrontのドメイン名を入力します(末尾に「.」を入れる必要があります)。
CloudFrontに独自ドメインを設定します。CloudFrontコンソールにアクセスしてディストリビューションの「一般」の「編集」ボタンをクリックします。
「代替ドメイン名(CNAME)-オプション」に独自ドメイン名(ここでは「atsushinotes.work」)を入力して、「カスタムSSL証明書-オプション」に先ほど作成した証明書を選択します。
「デフォルトルートオブジェクト-オプション」にユーザーがルートURL(/)にアクセスしたときに表示するファイルを設定します。今回は「atsushinotes.work/」にアクセスされたら「atsushinotes.work/index.html」を表示したいので、「index.html」を入力しました。
独自ドメインでアクセスして動作確認
atsushinotes.workにアクセスします。フォームページが表示されました。
送信ボタンを押してみます。成功しました。
DynamoDBを確認してみると、レコードが追加されています。
最後に
最後までお読みいただき、ありがとうございました。
独自ドメインアクセスをCloudFront経由アクセスにするところが一番つまづきました。証明書が必要だったり、エイリアスレコードとすることだったり。。
下記に参考にさせていただいたサイトをご紹介します。繰り返しですが、有識者の皆さまありがとうございました。
参考サイト
API GatewayとLambdaとDynamoDBを使ってサーバレス・最短で登録フォームを作る
S3 静的ウェブサイトにサーバーレスなお問い合わせフォームを実装してみた(Amazon SES + AWS Lambda + API Gateway)
AWS上の静的Webサイトの問合せフォームからメールを送信する(SES+Lambda+API Gateway+ちょっとRoute53)
AWSサーバーレス連絡フォームの作り方💪(AWS Lambda、API Gateway、SES)
コメント