こんにちは、広野です。
静的 WEB サイトは一方向の情報伝達ですが、とりあえず問い合わせフォームだけ付ければアクセスしてくれた人からの連絡手段を確立できます。
ちょっとした商品・サービス紹介用途であればそんな WEB サイトで十分なときもあります。
本記事では、AWS を使って以下のような簡易 WEB サイトをつくってみたいと思います。
- 静的 WEB サイトを独自ドメインでつくる
- 静的 WEB サイトへの通信は暗号化する
- 静的 WEB サイトはフリーの HTML テンプレートを使用する
- 静的 WEB サイトのホスティングやメール送信の仕組みは AWS を使用する
実現方法
アーキテクチャは以下です。
- 独自ドメインで WEB サイトを公開するため、Amazon CloudFront と Amazon Route 53 を使用します。
- 通信を暗号化するため、SSL証明書を AWS Certificate Manager で作成、Amazon CloudFront に関連付けます。
- 静的 WEB コンテンツは Amazon S3 でホスティングします。
- 静的 WEB サイト内に問い合わせフォームを作り、そこからメール送信スクリプト (JavaScript + jQuery) を呼び出してメールを送信します。
- メール送信には Amazon SES を使用しますが、命令を受け付ける API を Amazon API Gateway で作成し、Amazon SES に送信するメール内容を加工するために AWS Lambda 関数を関連付けます。
実装方法
独自ドメインの準備 (Amazon Route 53)
ドメイン名は、指定事業者と呼ばれるドメイン名の販売代理店?のような業者と契約して取得します。
AWS はドメインの使用権は提供してくれませんが、DNS レコードの管理は Amazon Route 53 で行うことができます。その方が AWS サービスと DNS の連携がしやすくなりますので、そのような準備をします。
AWS マネジメントコンソールから Amazon Route 53 の管理画面にアクセスし、取得したドメインを「パブリックホストゾーン」として登録します。登録すると、そのドメインに対してデフォルトで以下のような DNS レコードが登録された状態になります。
これだけでは、まだこのドメインは機能していません。ドメイン指定事業者側でこのドメインのネームサーバを AWS のネームサーバに変更する必要があります。AWS のネームサーバは、上記画面でタイプが NS の行に表示されている 4つです。
ドメイン指定事業者がお名前.comの場合ですが、契約者用の管理画面で以下のように AWS のネームサーバを登録します。
これで独自ドメインの DNS レコード管理が Amazon Route 53 の管理化に置かれたことになります。以降の作業は、この状態であることが前提となっています。
ただし DNS 関連の設定変更は、変更した設定が世界中のインターネットに伝播するまでに最大 72 時間かかることがあります。後続作業は念のため間を空けて予定するようにしましょう。通常は数時間で問題なく機能することが多いですが。
AWS 公式のドキュメントはこちら
SSL 証明書の準備 (AWS Certificate Manager)
WEB サイトへの通信を暗号化するため、SSL 証明書 (パブリック証明書) が必要になります。
AWS で証明書を作らなくてもよいのですが、AWS Certificate Manager で作成するとこれまた AWS サービスとの親和性が高いですし、期限更新も自動的にやってくれたり、すぐに発行、そして何より安い、などメリットが多すぎるので AWS ユーザは利用しない手はないです。
AWS マネジメントコンソールで、AWS Certificate Manager を開きます。AWS Certificate Manager はリージョンサービスなので、証明書を関連付けたい AWS リソースと同じリージョンで証明書を作成することになるのですが、今回関連付けるのは Amazon CloudFront です。その場合はバージニア北部リージョン (us-east-1) で作成する必要があることに注意してください。
さて、今回取得した独自ドメインの証明書を作成することになるのですが、私はよく「私が勝手に万能証明書と呼んでいる証明書」をつくっています。そのドメイン名のサイトがどんなサブドメイン名になっても共通して使える証明書です。
例えば、example.com というドメイン名を取得したとすると、
- aaa.example.com
- bbb.example.com
- ccc.ddd.example.com
- example.com
など、サブドメイン名がどのような名前でも、もしくは Zone Apex と言われるサブドメインなしの状態でも使用できる証明書です。これ1つつくっておくと、同じドメイン名のサイトを新たに立ち上げたときに証明書だけは共用できることになり手間が省けます。当然お財布にも優しいですしね。
AWS Certificate Manager の使用手順は AWS 公式ドキュメントを見てもらえたらと思いますが、万能証明書は以下のようにサブドメイン名を * のワイルドカードにして、追加で Zone Apex を指定する感じです。
証明書を作成する際、そのドメインの所有権の検証を求められます。Amazon Route 53 にそのドメインをパブリックホストゾーンとして登録済みですので、AWS Certificate Manager から指定された DNS レコードを登録するだけで検証は完了します。他にも所有権検証の方法はありますが、おそらくこれが最も早くて便利な方法です。
Amazon Route 53 への DNS レコード登録で検証をさせる場合は、証明書のステータス画面で「Route 53 でレコードを作成」のボタンを押すと自動的に検証用 DNS レコードを登録してくれます。
Amazon Route 53 の DNS レコードを見ると、自動的に CNAME レコードが追加されています。
この検証用 CNAME レコードを AWS Certificate Manager が読み取ると、証明書のステータスが「発行済み」となり、他の AWS リソースから使用可能な状態になります。
証明書まで作成できたら、証明書の ID (識別子) を控えておいてください。後の手順で使用します。
AWS 公式ドキュメントはこちら
メール送信環境準備 (Amazon SES)
AWS リソースからメールを送信するために、メールサーバの役割をしてくれるのが Amazon SES です。一般的には、SMTP サーバに送るという手続きではなく、Amazon SES に対して API でメール送信リクエストをします。
Amazon SES はリージョンサービスです。Amazon SES にリクエストをかける AWS リソースと同じリージョンの Amazon SES を使用するのがベストプラクティスです。ここで、使用するリージョンの Amazon SES に事前設定をしておく必要があります。
Amazon SES は、Sandbox と呼ばれる使用制限がデフォルトでかけられています。Sandbox を解除しない限り、メールの送信量や送信先に制限がかかります。したがって、想定する使用量を決めて AWS サポートに Sandbox 解除を依頼します。
Sandbox を解除すると、メールの送信先が制限されなくなります。
要件によっては、Sandbox を解除する必要はないかもしれません。その場合は、メール送信先を「検証済み ID」として Amazon SES にあらかじめ登録しておく必要があります。メールのドメイン名単位で登録するにはドメインの所有者確認があり厄介なので、数が少なければメールアドレス単位で登録するのが早いです。登録されたメールアドレスには、存在確認のため AWS からメールが飛びますのでご注意ください。そのメールに記載されたリンクをクリックすることで、登録が完了します。
Amazon SES 経由でメールを送信する際、メール送信元のメールアドレスを指定することになります。そのメールアドレスについては、Sandbox を解除していようがいまいが「検証済み ID」として登録されている必要があります。事前に実施しておきましょう。
WEB サイト環境構築 (AWS CloudFormation)
ここから先は、AWS CloudFormation で以下のサービスを連携させた状態でまとめて自動構築したいと思います。
- Amazon CloudFront + AWS Certificate Manager で作成した証明書 + Amazon Route 53 の DNS レコード
- Amazon S3
- Amazon API Gateway
- AWS Lambda
以下のパラメータを準備して、AWS CloudFormation でスタックを作成します。
- SiteName
WEB サイトの管理用の名前です。各種リソース名にこの名前が付加されます。 - DomainName
WEB サイトのドメイン名です。 - SubDomainName
WEB サイトのサブドメイン名です。このテンプレートでは、Zone Apex には対応していませんので必須です。 - FeedbackSenderEmail
問い合わせフォームがメールを送るときの送信元メールアドレスです。 - FeedbackReceiverEmail
問い合わせフォームがメールを送るときの送信先メールアドレスです。このテンプレートでは1つしか登録できません。 - CertificateId
AWS Certificate Manager で作成した証明書の ID です。
テンプレートはこちらです。
AWSTemplateFormatVersion: 2010-09-09 Description: The CloudFormation template that creates a static website hosting environment with a backend API for receiving emails from feedback form. # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: SiteName: Type: String Description: Your Site name. (Not any upper case characters) Default: xxxxx MaxLength: 40 MinLength: 1 DomainName: Type: String Description: Domain name for URL. Default: example.com MaxLength: 100 MinLength: 5 AllowedPattern: "([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\\.)+[a-zA-Z]{2,}" SubDomainName: Type: String Description: Sub domain name for URL. Default: xxxxx MaxLength: 20 MinLength: 1 FeedbackSenderEmail: Type: String Description: E-mail address that sends feedback emails. Default: sender@example.com MaxLength: 100 MinLength: 5 AllowedPattern: "[a-zA-Z0-9_+-]+(.[a-zA-Z0-9_+-]+)*@([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\\.)+[a-zA-Z]{2,}" FeedbackReceiverEmail: Type: String Description: E-mail address that receives feedback emails. Default: receiver@example.com MaxLength: 100 MinLength: 5 AllowedPattern: "[a-zA-Z0-9_+-]+(.[a-zA-Z0-9_+-]+)*@([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\\.)+[a-zA-Z]{2,}" CertificateId: Type: String Description: ACM certificate ID. CloudFront only supports ACM certificates in us-east-1 region. Default: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx MaxLength: 36 MinLength: 36 Resources: # ------------------------------------------------------------# # S3 # ------------------------------------------------------------# S3Bucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub website-${SiteName} PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true Tags: - Key: Cost Value: !Sub website-${SiteName} S3BucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref S3Bucket PolicyDocument: Version: "2012-10-17" Statement: - Action: - "s3:GetObject" Effect: Allow Resource: !Sub "arn:aws:s3:::${S3Bucket}/*" Principal: Service: cloudfront.amazonaws.com Condition: StringEquals: AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution} DependsOn: - S3Bucket - CloudFrontDistribution # ------------------------------------------------------------# # CloudFront # ------------------------------------------------------------# CloudFrontDistribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Enabled: true Comment: !Sub CloudFront distribution for website-${SiteName} Aliases: - !Sub ${SubDomainName}.${DomainName} HttpVersion: http2 IPV6Enabled: true PriceClass: PriceClass_200 DefaultCacheBehavior: TargetOriginId: !Sub S3Origin-website-${SiteName} ViewerProtocolPolicy: redirect-to-https AllowedMethods: - GET - HEAD - OPTIONS CachedMethods: - GET - HEAD CachePolicyId: !Ref CloudFrontCachePolicy OriginRequestPolicyId: !Ref CloudFrontOriginRequestPolicy ResponseHeadersPolicyId: !Ref CloudFrontResponseHeadersPolicy Compress: true SmoothStreaming: false DefaultRootObject: index.html CustomErrorResponses: - ErrorCachingMinTTL: 10 ErrorCode: 403 ResponseCode: 404 ResponsePagePath: /404.html Origins: - Id: !Sub S3Origin-website-${SiteName} DomainName: !Sub ${S3Bucket}.s3.${AWS::Region}.amazonaws.com S3OriginConfig: OriginAccessIdentity: "" OriginAccessControlId: !GetAtt CloudFrontOriginAccessControl.Id ConnectionAttempts: 3 ConnectionTimeout: 10 ViewerCertificate: AcmCertificateArn: !Sub "arn:aws:acm:us-east-1:${AWS::AccountId}:certificate/${CertificateId}" MinimumProtocolVersion: TLSv1.2_2021 SslSupportMethod: sni-only Tags: - Key: Cost Value: !Sub website-${SiteName} DependsOn: - CloudFrontCachePolicy - CloudFrontOriginRequestPolicy - CloudFrontResponseHeadersPolicy - CloudFrontOriginAccessControl - S3Bucket CloudFrontOriginAccessControl: Type: AWS::CloudFront::OriginAccessControl Properties: OriginAccessControlConfig: Description: !Sub CloudFront OAC for website-${SiteName} Name: !Sub OriginAccessControl-website-${SiteName} OriginAccessControlOriginType: s3 SigningBehavior: always SigningProtocol: sigv4 CloudFrontCachePolicy: Type: AWS::CloudFront::CachePolicy Properties: CachePolicyConfig: Name: !Sub CachePolicy-website-${SiteName} Comment: !Sub CloudFront Cache Policy for website-${SiteName} DefaultTTL: 3600 MaxTTL: 86400 MinTTL: 60 ParametersInCacheKeyAndForwardedToOrigin: CookiesConfig: CookieBehavior: none EnableAcceptEncodingBrotli: true EnableAcceptEncodingGzip: true HeadersConfig: HeaderBehavior: whitelist Headers: - Access-Control-Request-Headers - Access-Control-Request-Method - Origin QueryStringsConfig: QueryStringBehavior: none CloudFrontOriginRequestPolicy: Type: AWS::CloudFront::OriginRequestPolicy Properties: OriginRequestPolicyConfig: Name: !Sub OriginRequestPolicy-website-${SiteName} Comment: !Sub CloudFront Origin Request Policy for website-${SiteName} CookiesConfig: CookieBehavior: none HeadersConfig: HeaderBehavior: whitelist Headers: - Access-Control-Request-Headers - Access-Control-Request-Method - Origin QueryStringsConfig: QueryStringBehavior: none CloudFrontResponseHeadersPolicy: Type: AWS::CloudFront::ResponseHeadersPolicy Properties: ResponseHeadersPolicyConfig: Name: !Sub ResponseHeadersPolicy-website-${SiteName} Comment: !Sub CloudFront Response Headers Policy for website-${SiteName} SecurityHeadersConfig: ContentTypeOptions: Override: true FrameOptions: FrameOption: SAMEORIGIN Override: true ReferrerPolicy: Override: true ReferrerPolicy: strict-origin-when-cross-origin StrictTransportSecurity: AccessControlMaxAgeSec: 31536000 IncludeSubdomains: true Override: true Preload: true XSSProtection: ModeBlock: true Override: true Protection: true # ------------------------------------------------------------# # Route 53 # ------------------------------------------------------------# Route53RecordIpv4: Type: AWS::Route53::RecordSet Properties: HostedZoneName: !Sub ${DomainName}. Name: !Sub ${SubDomainName}.${DomainName}. Type: A AliasTarget: HostedZoneId: Z2FDTNDATAQYW2 DNSName: !GetAtt CloudFrontDistribution.DomainName DependsOn: - CloudFrontDistribution Route53RecordIpv6: Type: AWS::Route53::RecordSet Properties: HostedZoneName: !Sub ${DomainName}. Name: !Sub ${SubDomainName}.${DomainName}. Type: AAAA AliasTarget: HostedZoneId: Z2FDTNDATAQYW2 DNSName: !GetAtt CloudFrontDistribution.DomainName DependsOn: - CloudFrontDistribution # ------------------------------------------------------------# # Lambda # ------------------------------------------------------------# LambdaSendFeedbackByEmail: Type: AWS::Lambda::Function Properties: FunctionName: !Sub SendFeedbackByEmail-website-${SiteName} Description: !Sub Lambda Function to send user's feedback email for website-${SiteName} Runtime: python3.9 Timeout: 3 MemorySize: 128 Role: !GetAtt LambdaSesInvocationRole.Arn Handler: index.lambda_handler Tags: - Key: Cost Value: !Sub website-${SiteName} Code: ZipFile: !Sub | import json import datetime import boto3 def datetimeconverter(o): if isinstance(o, datetime.datetime): return str(o) def lambda_handler(event, context): try: # Define Variables ses = boto3.client('ses',region_name='${AWS::Region}') d = json.loads(event['body']) print({"ReceivedData": d}) RECEIVERS = [] RECEIVERS.append('${FeedbackReceiverEmail}') SENDER = '${FeedbackSenderEmail}' # Send Email res = ses.send_email( Destination={ 'ToAddresses': RECEIVERS }, Message={ 'Body': { 'Text': { 'Charset': 'UTF-8', 'Data': 'NAME: ' + d['name'] + '\nMAILADDRESS: ' + d['email'] + '\n\nMESSAGE:\n' + d['body'] } }, 'Subject': { 'Charset': 'UTF-8', 'Data': d['subj'] + ' (${SubDomainName}.${DomainName})' } }, Source=SENDER, ReplyToAddresses=[d['email']] ) except Exception as e: print(e) return { 'isBase64Encoded': False, 'statusCode': 200, 'body': str(e) } else: return { 'isBase64Encoded': False, 'statusCode': 200, 'body': json.dumps(res, indent=4, default=datetimeconverter) } # ------------------------------------------------------------# # API Gateway # ------------------------------------------------------------# HttpApiSendFeedbackByEmail: Type: AWS::ApiGatewayV2::Api Properties: Name: !Sub SendFeedbackByEmail-website-${SiteName} Description: !Sub HTTP API to send user's feedback email for website-${SiteName} ProtocolType: HTTP CorsConfiguration: AllowCredentials: false AllowHeaders: - "*" AllowMethods: - POST - OPTIONS AllowOrigins: - !Sub https://${SubDomainName}.${DomainName} ExposeHeaders: - "*" MaxAge: 600 DisableExecuteApiEndpoint: false Tags: Cost: !Sub website-${SiteName} HttpApiIntegrationSendFeedbackByEmail: Type: AWS::ApiGatewayV2::Integration Properties: ApiId: !Ref HttpApiSendFeedbackByEmail IntegrationMethod: POST IntegrationType: AWS_PROXY IntegrationUri: !GetAtt LambdaSendFeedbackByEmail.Arn CredentialsArn: !GetAtt ApiGatewayLambdaInvocationRole.Arn PayloadFormatVersion: 2.0 TimeoutInMillis: 5000 HttpApiRouteSendFeedbackByEmail: Type: AWS::ApiGatewayV2::Route Properties: ApiId: !Ref HttpApiSendFeedbackByEmail RouteKey: POST /SendFeedbackByEmail Target: !Sub integrations/${HttpApiIntegrationSendFeedbackByEmail} HttpApiStageSendFeedbackByEmail: Type: AWS::ApiGatewayV2::Stage Properties: ApiId: !Ref HttpApiSendFeedbackByEmail AutoDeploy: true StageName: $default # ------------------------------------------------------------# # Lambda SES Invocation Role (IAM) # ------------------------------------------------------------# LambdaSesInvocationRole: Type: AWS::IAM::Role Properties: RoleName: !Sub LambdaSesInvocationRole-website-${SiteName} Description: This role allows Lambda functions to invoke SES. AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSESFullAccess - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess # ------------------------------------------------------------# # API Gateway Lambda Invocation Role (IAM) # ------------------------------------------------------------# ApiGatewayLambdaInvocationRole: Type: AWS::IAM::Role Properties: RoleName: !Sub ApiGatewayLambdaInvocationRole-website-${SiteName} Description: This role allows API Gateways to invoke Lambda functions. AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - apigateway.amazonaws.com Action: - sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaRole - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess - arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess # ------------------------------------------------------------# # Output Parameters # ------------------------------------------------------------# Outputs: # S3 Bucket S3BucketName: Value: !Ref S3Bucket # API Gateway APIGatewayEndpointSendFeedbackByEmail: Value: !Join - "" - - !GetAtt HttpApiSendFeedbackByEmail.ApiEndpoint - /SendFeedbackByEmail # Site URL SiteURL: Value: !Sub https://${SubDomainName}.${DomainName}
スタック完成後、出力タブで Amazon S3 バケット名と Amazon API Gateway エンドポイント名を確認しておいてください。後の作業で使用します。
静的 WEB コンテンツ準備
ここまでで、バックエンドの仕組みはできました。ここからは、フロントエンドの作業に入ります。
静的 WEB サイトのコンテンツにはフリーの HTML テンプレートを使用します。以下の提供サイトから「Forty」というテンプレートをダウンロードしてみましょう。
ダウンロードした ZIP ファイルを解凍すると、以下のようなフォルダ、ファイルがあります。
この中の、index.html をダブルクリックして直接ブラウザで表示してみましょう。
下にスクロールしていくと、問い合わせフォームが見つかります。
この時点では問い合わせフォームはただの枠なので全く動作しませんが、先ほど作成したメール送信の仕組みを連動させるようにします。
メール送信用スクリプト準備 (JavaScript & jQuery)
問い合わせフォームに入力し「SEND MESSAGE」ボタンを押すとメールが送信されるようなスクリプトを、この HTML テンプレートに追加で仕込みます。
feedback.js
以下のスクリプトファイルを新規に作成します。
定数定義の箇所にある変数の値は環境に応じて修正します。
SUBJECT には、送信されるメールの件名を入力します。
URL には、メールを送信するための API の URL を入力します。ここでは、先ほど AWS CloudFormation で構築した Amazon API Gateway のエンドポイント URL になります。
const sendMessageButton = document.getElementById('sendMessageButton'); sendMessageButton.addEventListener('click', (e) => { // inputタグのデフォルトの動作を無効化 e.preventDefault(); // 定数定義 const SUBJECT = "WEBサイトからの問い合わせ"; const URL = "https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/SendFeedbackByEmail"; // 入力エラーチェック const name = $("#name").val(); if (!name) { alert ("名前を入力して下さい。"); return; } const email = $("#email").val(); if (!email) { alert ("メールアドレスを入力して下さい。"); return; } const message = $("#message").val(); if (!message) { alert ("メッセージを入力して下さい。"); return; } if (!email.match(/^[a-zA-Z0-9]+[a-zA-Z0-9\._\-]*@[a-zA-Z0-9_\-]+[a-zA-Z0-9\._-]*\.[a-z]+$/)) { alert ("メールアドレスの形式が誤っているようです。"); return; } // 送信処理 const data = { "subj": SUBJECT, "name": name, "email": email, "body": message }; $.ajax({ type: "POST", cache: false, url: URL, crossDomain: true, contentType: "application/json; charset=utf-8", data: JSON.stringify(data), dataType: "json", timespan: 5000 }).done(function(data1,textStatus,jqXHR) { //通信成功時 alert("お問い合わせを送信しました。"); document.getElementById("contact-form").reset(); }).fail(function(jqXHR, textStatus, errorThrown) { //失敗時 alert("お問い合わせの送信に失敗しました。"); }).always(function(jqXHR, textStatus, errorThrown){ //成功、失敗関わらず実行 }); });
このコードは JavaScript で書かれていますが、API に POST するために ajax を使っています。これは、jQuery モジュールがあらかじめロードされていないと動きません。その他、jQuery モジュールが存在する前提で書かれているコードがいくつかあります。
実はこの HTML テンプレートには、あらかじめ jQuery モジュールが適切にロードされるように最初から仕込まれています。テンプレートのファイル一式を確認すると、assets/js フォルダに jquery.min.js というファイルがあります。これが jQuery モジュールです。スクラッチから HTML コンテンツを作成する場合には、公式サイトからダウンロードして配置する必要があります。
その他、画面レイアウト調整のために用意されたスクリプトが格納されています。作成した feedback.js はこの assets/js フォルダに保存しておきましょう。後ほど HTML との関連付けの際に保存先パスを指定することになります。
index.html
作成した feedback.js は、問い合わせフォームに入力した内容を読み取ってメール送信 API に投げてくれるスクリプトですが、作成しただけではボタンを押したときに呼び出されません。
その関連付けをするために、index.html を修正します。
- 修正箇所(黄色マーカー部分)
<form method="post" action="#"> <div class="fields"> <div class="field half"> <label for="name">Name</label> <input type="text" name="name" id="name" /> </div> <div class="field half"> <label for="email">Email</label> <input type="text" name="email" id="email" /> </div> <div class="field"> <label for="message">Message</label> <textarea name="message" id="message" rows="6"></textarea> </div> </div> <ul class="actions"> <!-- SEND MESSAGE ボタンにid属性を追加 --> <li><input type="submit" value="Send Message" class="primary" id="sendMessageButton" /></li> <li><input type="reset" value="Clear" /></li> </ul> </form> <!-- 中略 --> <!-- Scripts --> <script src="assets/js/jquery.min.js"></script> <script src="assets/js/jquery.scrolly.min.js"></script> <script src="assets/js/jquery.scrollex.min.js"></script> <script src="assets/js/browser.min.js"></script> <script src="assets/js/breakpoints.min.js"></script> <script src="assets/js/util.js"></script> <script src="assets/js/main.js"></script> <!-- feedback.js をscript定義の最後尾に追加 --> <script src="assets/js/feedback.js"></script>
これにて、用意したメール送信用スクリプトが SEND MESSAGE ボタンを押したときに発動するようになります。
静的 WEB コンテンツ配置
ここまでに作成、修正したファイルを含むテンプレートファイルを一式、作成した Amazon S3 バケットに配置します。
設定したドメイン名の URL にブラウザでアクセスすると、無事静的 WEB コンテンツが表示されました。
もし Amazon S3 バケットに配置したコンテンツを更新した場合、Amazon CloudFront にはキャッシュが残ってしまっているため、更新がすぐに反映されません。コンテンツ更新後、公開されるコンテンツにすぐに反映させたい場合は、Amazon CloudFront のキャッシュをクリアしてください。
以下、AWS 公式ドキュメントによるキャッシュクリアの手順です。ファイルの無効化と翻訳されていますが、キャッシュがクリアされるだけなのでご心配なく。削除対象のキャッシュは /* を指定することで全てクリアされます。
問い合わせフォーム動作確認
実際に問い合わせフォームの動作確認をしてみましょう。以下のように問い合わせフォームに入力して SEND MESSAGE ボタンを押してみます。
正常にメール送信 API にリクエストが送信されれば、以下のようにアラートが表示されます。
実際に、このようなメールが届きます。
めでたし、めでたし。
まとめ
いかがでしたでしょうか?
問い合わせフォーム付きの簡易な静的 WEB サイトをサーバーレスでつくるための一連の手順を紹介いたしました。今回は動的な機能を jQuery で作成しましたが、この程度のちょっとした機能であれば jQuery は非常に便利です。SPA 環境をつくるまでもないかな、と。
本記事が皆様のお役に立てれば幸いです。