mattintosh note

どこかのエンジニアモドキの備忘録

CloudFront Functionsでクエリ付きリクエストのリダイレクト

前に「こういうの作って」って言われて作ってメモってたんだけど何やってるかわからなくなったので整理。

CloudFront Functions で単純にリダイレクトする場合は ${event.request.uri} を引き継げば良い。

function handler (event) {
    var host = 'example.com';
    var url = `https://${host}${event.request.uri}`;

    var response = {
        statusCode: 301,
        statusDescription: 'Moved Permanently',
        headers: {
            location: {
                value: url,
            },
        },
    };

    return response;
}

ただしこれは https://example.com/foo?bar=1 のようなリクエストだった場合に https://example.com/foo になる。

クエリも一緒にリダイレクトしたい場合 event.request.querystring を分解しなければならない。面倒なのが ?q1=a&q1=b みたいな場合は multiValue というものになってしまうところ。

/index.html?q1=1&q2=2&q3=a&q3=b のようなリクエストだった場合の eventの中身(JSON.stringify(event) したもの)。

{
  "version": "1.0",
  "context": {
    "distributionDomainName": "d123.cloudfront.net",
    "distributionId": "E123",
    "eventType": "viewer-request",
    "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
  },
  "viewer": {
    "ip": "1.2.3.4"
  },
  "request": {
    "method": "GET",
    "uri": "/index.html",
    "querystring": {
      "q1": {
        "value": "1"
      },
      "q2": {
        "value": "2"
      },
      "q3": {
        "value": "a",
        "multiValue": [
          {
            "value": "a"
          },
          {
            "value": "b"
          }
        ]
      }
    },
    "headers": {},
    "cookies": {}
  }
}

一応 CloudFront Functions では querystring が使えるらしい。

docs.aws.amazon.com

とりあえず event.request.querystringObject.entries() で回してみる。

Object.entries(event.request.querystring).forEach(function (q) {
     console.log(q);
});

で、こうなるわけだけど valuemultiValue は両方存在している必要はあるのだろうか…。

['q1',{value:'1'}]
['q2',{value:'2'}]
['q3',{value:'a',multiValue:[{value:'a'},{value:'b'}]}]

querystring.stringify() に渡すためには上記の multiValue を分解してこんな感じにする必要がある。

{
    q1: '1',
    q2: '2',
    q3: [
        'a',
        'b'
    ]
}

関数をごにょごにょ書く。

var querystring = require('querystring');

function handler (event) {
    var host = 'example.com';
    var url = `https://${host}${event.request.uri}`;

    if (Object.keys(event.request.querystring).length) {
        var query = {};
        Object.entries(event.request.querystring).forEach(function (q) {
            query[q[0]] = q[1].multiValue ? q[1].multiValue.map(function (m) { return m.value; }) : q[1].value;
        });
        url = url + '?' + querystring.stringify(query);
    }

    var response = {
        statusCode: 301,
        statusDescription: 'Moved Permanently',
        headers: {
            location: {
                value: url,
            },
        },
    };

    return response;
}

あとは適当なカスタムオリジン(example.com とか)を適当に作成して CloudFront をとりあえず作成したらあとはこの関数を Viewer Request にセットすれば OK。


こちらのコードを参考にさせていただきました。

tech.enechange.co.jp