こんにちは!エンジニア2年目、フロントエンドを勉強中のmiuraです。
この記事はAmazon Web Services Advent Calendar 2017の13日目の記事となります。
私は最近、Angularを使ったフロントエンド開発やAWSでのサーバーレス開発をしているのですが、
サーバーレスのWebアプリを構築するとなった際につまった部分がありました。
それは、
WebアプリをS3に、サーバーレスをAPI GatewayとAWS Lambdaで構成すると双方のURLが違うため、
ブラウザではSame-Origin Policy (SOP)が適用されてエラーになってしまうことです。
これを回避するためにはCross-Origin Resource Sharing (CORS)の設定を行う必要があります。
今回は、そのCORSの設定を含めてAWS上で一通りの動きができるWebアプリを作っていきます。
全体構成
全体構成は以下の図のようなシンプルな構成を作ります。
サーバーレス
S3にWebアプリをアップロードし、静的ウェブホスティングを設定してWebアプリにアクセス出来るようにします。
そして、API Gatewayを通してAWS Lambdaから"Hello, World!"というメッセージを取得出来るようにします。
Angularを使ったWebアプリ
今回はサンプルとして、Angularの公式ページにあるQuick Startを引用します。
https://angular.io/guide/quickstart
このQuick Startはタイトルにある"Welcome to app!"の"app"部分がAngularによってinjectしているサンプルになっています。
そこで今回、この"app"の部分をAWS Lambdaから取得した"Hello, World!"に変えていきたいと思います。
サーバーレス
Serverless Frameworkのプロジェクト生成
Serverless FrameworkとはをYAMLファイルに簡単な記述を行えば、コマンド1つでAWS環境を構築できるツールです。
※AWS CLIのセットアップが必要ですが、いろんなサイトで紹介されているので今回は割愛します。
以下のコマンドを実行すれば、簡単にPython3系用のサンプルプロジェクトが生成されます。
npm install -g serverless serverless create --template aws-python3 --path my-serverless
AWS Lambda用ソースコードの作成
では、Serverless Frameworkで生成されたhandler.pyを編集して、
通信先としてのレスポンスを返すAWS Lambdaのソースコードを書いていきます。
このときに、注意すべきは冒頭でも書いたCORSの設定です。
CORSとは、データのアクセスを許可できるWebサイトに対してOriginを越えたアクセスを可能にするための仕組みで、
HTTPレスポンスヘッダに"Access-Control-Allow-Origin"として信頼するWebサイトのOriginを付与することでブラウザ側でアクセスが許可されます。
今回許可するWebサイトのOriginは後述するWebアプリを設置するS3のエンドポイントになります。
my-serverless/handler.py
import json def hello(event, context): body = { "message": "Hello World!" } response = { "statusCode": 200, "body": json.dumps(body), "headers": { "Access-Control-Allow-Origin": "Webアプリを置いたS3のエンドポイント" # CORS設定 } } return response
API Gatewayの設定
次にHTTP通信の受け口であるAPI Gatewayの設定をします。
my-serverless/serverless.yml
service: my-serverless provider: name: aws runtime: python3.6 region: us-west-2 apiKeys: # APIキーを生成 - api-key functions: hello: handler: handler.hello events: - http: # API Gatewayの設定 path: hello method: get private: true # APIキーを使用する cors: true # API GatewayにCORSの設定をする
Angularを使ったWebアプリ
Angularプロジェクトの生成
以下のコマンドを実行して、Angularのサンプルプロジェクトを生成します。
npm i -g @angular/cli ng new my-app
HTTP通信用サービスの作成
HTTP通信を行うためのAngularのサービスを作成します。
HTTP通信用のクラスとしてHttpClient, HttpHeadersをインポートして使用します。
my-app/src/app/app.service.ts
import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; @Injectable() export class AppService { constructor(private http: HttpClient) { } getMessage(callback: (msg: string) => void): void { // API GatewayとHTTP通信して、取得成功時にコールバックを行なう this.http.get('API GatewayのURL', { headers: new HttpHeaders().set('x-api-key', 'APIキー') }).subscribe(data => { callback(data['message']); }); } }
次にWebアプリのモジュールにサービスで使ったHttpClient, HttpHeadersのモジュールであるHttpClientModuleをインポートします。
また、作成したサービスAppService も登録します。
my-app/src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; import { AppService } from './app.service'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, HttpClientModule // HTTP通信モジュールをインポート ], providers: [AppService], // 作成したサービスを登録 bootstrap: [AppComponent] }) export class AppModule { }
そして、ページ表示の内容や動作を定義しているコンポーネントに作成したサービスを呼び出す処理と、
HTTP通信成功時の処理を記述します。
my-app/src/app/app.component.ts
import { Component } from '@angular/core'; import { AppService } from './app.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], providers: [AppService] }) export class AppComponent { constructor(private appService: AppService) { } title = 'app'; ngOnInit(): void { this.appService.getMessage(msg => { this.title = msg; // HTTP通信成功時にタイトルに取得したメッセージを表示する }) } }
ビルド
以下のコマンドを実行すると本番環境用のビルドが行われます。
ビルド結果はmy-app/distフォルダに出力されます。
cd my-app ng build --prod
S3の静的ウェブホスティング
AWSコンソールでS3を開き、バケットを作成します。
※今回はmy-web-applicationという名前で作成しています。
そして、その作成したS3バケットの中にビルド結果のmy-app/distフォルダ内のファイルをアップロードします。
次にバケットポリシーをブラウザから直接S3バケットの中のオブジェクトを取得出来るように設定します。
バケットのアクセス権限タブ内にあるバケットポリシーに以下を設定します。
Resourceには作成したバケットのARNを指定します。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::my-web-application/*" } ] }
最後にバケットのプロパティタブにあるStatic website hostingを選択します。
「このバケットを使用してウェブサイトをホストする」をチェックし、
インデックスドキュメントにindex.htmlを入力して保存します。
これにより、静的ウェブホスティングの設定画面の上部に表示されているエンドポイントにアクセスすると、
S3に保存されたWebアプリが表示されました。
また、表示される際にHTTP通信をして"Hello World!"が取得され、タイトルに表示されました。
まとめ
今回、ServerlessFrameworkやAngularを使ったサーバーレスのWebアプリの作成をしました。
その際、S3にあるWebアプリからAPI Gatewayを通してデータの受け渡しを行なうために、
サーバーレス側にCORS設定方法を紹介しました。
試しにサーバーレスの構成でWebアプリを作ってみたい人はぜひ参考にしてみてください。
それでは。
Acroquest Technologyでは、キャリア採用を行っています。
- ビッグデータ(Hadoop/Spark、NoSQL)、データ分析(Elasticsearch、Python関連)、Web開発(SpringCloud/SpringBoot、AngularJS)といった最新のOSSを利用する開発プロジェクトに関わりたい。
- マイクロサービス、DevOpsなどの技術を使ったり、データ分析、機械学習などのスキルを活かしたい。
- 社会貢献性の高いプロジェクトや、顧客の価値を創造するようなプロジェクトで、提案からリリースまで携わりたい。
- 書籍・雑誌等の執筆や、対外的な勉強会の開催・参加を通した技術の発信、社内勉強会での技術情報共有により、エンジニアとして成長したい。
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。【データ分析】
データ分析案件で時系列データの異常検知に挑戦したいエンジニアWanted! - Acroquest Technology株式会社のエンジニア中途・インターンシップ・契約・委託の求人 - Wantedlywww.wantedly.com