LoginSignup
32

More than 5 years have passed since last update.

JAWS FrameworkでWebブラウザからS3にファイルをアップロードして、有効期限付きのダウンロード付きURLとパスワードを生成するサービスを作ってみた、時の開発メモ

Last updated at Posted at 2015-10-26

はじめに

JAWS Frameworkを使ってお手軽にLambda+API GatewayでHelloWorldするメモ で、
とりあえず感触は掴めたので、実際にちょっとしたWebServiceを作ってみました。

前提として、上記記事の手順は経ているものとして、
JAWSを使ったサービス開発の時のハマリポイントや悩んだことのメモを共有します。

作ったWebServiceの概要

機能

  • ファイルをWebブラウザからS3へUpload (Downloadパスワードと有効期限を指定)
    • Upload Keyというのが別途必要で、 期限付きUpload KeyMaster Key から作成できる
  • 有効期限付きのDownload Link が 表示される
  • そのLinkへ行くと、パスワードが求められ、有効期限内ならばファイルがDownloadできる
  • Uploadしたファイルは7日間で自動的に削除される(有効期限とは無関係)

cap.png

仕組み

  • API Gateway + Lambda + S3 で動いている

File Upload時の動作

  1. createsessiontoken API を呼びだして Upload用のCredentialを取得
  2. ブラウザから直接S3にUpload (パスワードはこのURL(S3 ObjectKey)に反映されている)
  3. Uploadした ObjectKey で creategetobjecturl API を呼びだして S3の期限付きURLを作成
  4. S3の期限付きURL をちょっと加工して、Download URLを作成

File Download時の動作

  1. Formに入力してもらうパスワードとDownload URLに埋めてあるパラメータから、 S3の期限付きURL を復元してRedirect
    • パスワードが間違っていると S3からエラーと冷たく言われる・・・

ソースコード

【本題】JAWSを使った開発の時のハマリポイントや悩んだこと

  • JAWS version: 1.3.3

参考になるソースコード

なかなか情報が少ないですが、
https://github.com/awsm-org/awsm-images がありました。

sha256などのパッケージの導入方法

今回 sha256 の npm パッケージを使ったのですが、これを index.js からどう参照するのか、ということです。

そんなに難しいことはなく、

npm install --save sha256 

として、

var sha256 = require("sha256");

などとすればOKでした。

環境情報の入れ方、参照方法

これがなかなか悩みました。

設定や取得や一覧は マニュアルにも書いてあるとおり、

% jaws env set dev all <KEY> <VAL>

などでいけますが、プログラム中からの参照をどうすれば良いのか。

わかれば簡単で、 HASH_SECRET という環境変数は process.env.HASH_SECRET で参照できます。

awsm.jsonのenvVarsの意味

最初 awsm.jsonのenvVarsの意味がわからなかったのですが、プログラム中で環境変数を参照している場合ここに記述しておくと、

% jaws env list dev all

としたときに 「定義されていない変数を警告してくれる」 機能があります。
定義し忘れると、環境が変わった時に実行時エラーになって辛いので、忘れずに書いておくと良いと思います。

自前ロジックの共有

複数の Lambda Functionから参照される自前のロジックをどこにおいてどう共有すればよいか。

ですが、 index.js の 一つ上のDirに upload_key.js などとして

upload_key.js
var UploadKey = function(secret) { 
    ...
};

exports.UploadKey = UploadKey;

とかいておけば、

index.js
var AWS = require('aws-sdk'),
    keyUtil = require('../upload_key').UploadKey(process.env.HASH_SECRET);

というように参照できます。

Error Responseの定義

Web APIとして動作する場合には、HTTP Statusが大きな意味を持ちます。
LambdaのResponse をどうやって、 API Gatewayの HTTP Statusとして反映させるか。

いくつか方法がありますが、 awsm.json

awsm.json
      "Responses": {
        "400:.*": {
          "statusCode": "400",
          "responseParameters": {
            "method.response.header.Access-Control-Allow-Origin": "'*'"
          }
        },

と書いておくと、 callbackのエラー文字列に 400:.* の時に Status 400にMappingしてくれる定義を作ってくれます。(デプロイしたあとAPI Gatewayの設定を見るとわかります)
API_Gateway.png

そうすると、 cb('400: Bad Request!!', null); という Callbackを返せば、HTTP Statusが400でクライアント側に返るようになります。

CORSの設定

Web BrowserからAPIを呼び出す場合、 CORSの対応をする必要がある場合があります。

それも awsm.jsonresponseParameters にこのように

awsm.json
        "default": {
          "statusCode": "200",
          "responseParameters": {
            "method.response.header.Access-Control-Allow-Origin": "'*'",
            "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization'",
            "method.response.header.Access-Control-Allow-Methods": "'POST'"
          },
          "responseModels": {},
          "responseTemplates": {
            "application/json": ""
          }
        }

書いておくとGETなどのときはOKなのですが、
POST リクエストの場合に発生する pre-flight の OPTIONS メソッドに対する定義を同時に行うことができません。
https://github.com/jaws-framework/JAWS/issues/222
などでも話されているようですが、現時点ではJAWSではまだ未対応のようです。
※ 対応されるのは時間の問題でしょうが…

AWS の WebConsoleでAPI Gatewayの設定を行えばOKなのですが、
多少楽ができる方法を書いておきます.

JAWSを使って OPTIONSの設定を少しだけ楽をする

  1. 対象APIのawsm.jsonのMEHTODを OPTIONS に変更して endpoint をデプロイする(→ API GatewayのMethodにOPTIONSが追加される(POSTも残っている))
  2. このままだとエラーになるので、もう一度 POST などと書き戻して endpoint をデプロイする(API Gatewayの OPTIONSの設定は残る)
  3. AWS の WebConsoleを開く
  4. Integration Request の Type を Mock に変更する

    API_Gateway.png

  5. 今度はIntegration Responseの中を開いて, Header MappingsMapping valueが空になっている(Mockにしたから)ので、そこを適切に埋める

API_Gateway.png

  1. WebConsoleから手動でAPIをデプロイする

という感じです。
一回だけやっておけばOKなので、まあなんとか。

event.jsonの意味

(追記:2015/10/27)
コメント頂いたのでここにも書いておきます。
event.jsjaws run を実行した時に index.js

index.js
// Export For Lambda Handler
module.exports.run = function (event, context, cb) {
...

event に渡されるJSONみたいです。Localの簡易動作確認用です。
jaws runは例えばこんな感じに使います。

cmd
cd aws_modules/fileexchange/creategetobjecturl
jaws run

値を変えてテストしようとすると、
今は毎回event.jsonを書き換えないといけないのが少し面倒ですが、いずれ引数でファイルを指定できたりいろいろ工夫がされるのではないかと思います。

※ TOPのディレクトリにtests/ というのも作られるけど、使い方は不明・・・

API GatewayからLambdaへの QueryString や POST Dataの渡し方

(追記:2015/10/27)

API Gatewayの書き方が独特で私はあまり詳しくないですが、例えばこういう風に書くことができます

GET リクエストの QueryStringの場合

awsm.json
      "RequestTemplates": {
        "application/json": "{\"key\":\"$input.params('key')\", \"objKey\":\"$input.params('objKey')\"}"
      },

Escapeが色々酷くて見難いですが、
これによって、 QueryStringの keyobjKey の値が、 event.keyevent.objKey にMappingされてLambda側に渡ってきます。

どういう書き方ができるかはAWS公式のDocument を見ると良いと思います。

POSTリクエストでBODYがJSON形式の場合

この場合何も書かなくてもそのまま Lambda側の event Objectに渡ってくるようです。
深く理解できてないので、なんでなのかよくわかりませんが。
この辺は今後もちょくちょく仕様が変わるんじゃないかなぁ、という気がします。

さいごに

このくらいのサービスをだいたい1日で作れたので、JAWSはなかなか実用的だと思います。
最初は戸惑いますが、慣れると試行錯誤サイクルも早くなるし、やってみる価値はあると思います!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32