短縮URL生成ページを作ろう

GoogleのURL短縮サービスが終了する(2019/3/30まで)そうで、Firebaseで代用するのが妥当なようです。
Webページから作成できるように、短縮URL作成用のページをさくっと作ってみましょう。

# ダイナミックリンクドメインの作成

まずは、Firebaseコンソールから、自分用にダイナミックリンクドメインを作成します。
下記のような感じで、XXXXにサブドメイン名を割り当ててもらいます。

　https://XXXX.page.link

短縮URLはこのドメイン名をベースに生成されます。

Firebaseコンソールを開きます。
https://console.firebase.google.com/

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/083636ba-2c80-dcdf-0d0a-ed048f1023c8.png)

いづれかのプロジェクトを選択します。作成していない場合は、プロジェクトを追加 を選択して作成します。
次に、左側のナビゲーションから、拡大 → Dynamic Links を選択します。
ドメインの追加 を選択します。（初めてここに来た方は、始めるボタンを押下すると、ドメインの追加が始まるはず）

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/8fa75f9d-a1af-835e-b4dc-2bb2462c038d.png)

ここで、好きな名前のサブドメイン名を指定します。せっかく短縮URLなので、なるべく短い方がよいかと思います。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/f4839e24-a10d-024d-d017-628368457aee.png)

次に、左側ナビゲーションの、Project Overviewの右側の歯車のマークを選択します。そうすると、ウェブAPIキーが表示されていますので、それをメモしておきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/3c3571a6-d29d-9097-fdc7-ddbc078b4665.png)

# RESTfulサーバを立ち上げる

RESTful化します。
なぜ、RESTfulサーバを挟むか、API Keyがばれないように、です。

いつもながら、以下を参考にしてください。
[SwaggerでRESTful環境を構築する](https://qiita.com/poruruba/items/72d8c85f8923bc78b918)

Swagger定義ファイルの該当部分の抜粋です。

```:swagger.yaml
paths:
  /urlshorter:
    post:
      x-swagger-router-controller: routing
      operationId: urlshorter
      parameters:
        - in: body
          name: UrlShorter
          required: true
          schema:
            $ref: '#/definitions/UrlShorterRequest'
      responses:
        200:
          description: Success
          schema:
            type: object

definitions:
  UrlShorterRequest:
    type: object
    required:
    - targetUrl
    properties:
      targetUrl:
        type: string
```

以下が、実装部分です。

```:index.js
var fetch = require('node-fetch');
const Response = require('../../helpers/response');

const FIREBASE_API_KEY = 【FirebaseのプロジェクトのウェブAPIキー】;
const DYNAMICLINKS_DOMAIN = 'https://【ダイナミックリンクのサブドメイン名】.page.link';

exports.handler = async (event, context, callback) => {
    console.log(event);

    if( event.path == '/urlshorter'){
        var body = JSON.parse(event.body);
        var url = decodeURIComponent(body.targetUrl);
        var param = {
            "dynamicLinkInfo": {
                "domainUriPrefix" : DYNAMICLINKS_DOMAIN,
                "link" : url
            }
        };
    
        var url = 'https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=' + FIREBASE_API_KEY;
        return fetch(url, {
            method : 'POST',
            body : JSON.stringify(param),
            headers: { 'Content-Type': 'application/json' }
        })
        .then((response) => {
            return response.json();
        })
        .then(json =>{
            console.log(json);
    
            return new Response( json );
        });
    }
}
```

ユーティリティも示しておきます。

```:response.js
class Response{
    constructor(context){
        this.statusCode = 200;
        this.headers = {'Access-Control-Allow-Origin' : '*'};
        if( context )
            this.set_body(context);
        else
            this.body = "{}";
    }

    set_error(error){
        this.body = JSON.stringify({"err": error});
        return this;
    }

    set_body(content){
        this.body = JSON.stringify(content);
        return this;        
    }
    
    get_body(){
        return JSON.parse(this.body);
    }
}

module.exports = Response;
```

以下の部分を各人の内容に合わせて修正してください。

【FirebaseのプロジェクトのウェブAPIキー】;
【ダイナミックリンクのサブドメイン名】

(参考)
https://firebase.google.com/docs/reference/dynamic-links/link-shortener

# フロントWebページの作成

HTML部分です。

```:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta http-equiv="Content-Security-Policy" content="default-src * data: gap: https://ssl.gstatic.com 'unsafe-eval' 'unsafe-inline'; style-src * 'unsafe-inline'; media-src *; img-src * data: content: blob:;">
  <meta name="format-detection" content="telephone=no">
  <meta name="msapplication-tap-highlight" content="no">
  <meta name="apple-mobile-web-app-capable" content="yes" />
  <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">

  <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
  <!-- Latest compiled and minified CSS -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
  <!-- Optional theme -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
  <!-- Latest compiled and minified JavaScript -->
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

  <title>短縮URL</title>

  <script src="https://unpkg.com/vue"></script>
</head>
<body>
    <div id="top" class="container">
        <h1>短縮URL</h1>

        <div class="form-group">
            <label>targetUrl</label>
            <input type="text" class="form-control" v-model="target_url">
            <button class="btn btn-primary" v-on:click="make_shorturl()">短縮URLの生成
        </div>
</button> 
        <br>
        ＜生成結果＞
        <div class="form-group">
            <label>短縮URL:</label> {{shorter_url}}
        </div>
        ホワイトリストの変更はこちら<br>
        <a href="https://console.firebase.google.com/">Firerebaseコンソール</a>

    </div>
    <script src="js/start.js"></script>
</body>
```

Javascript部分です。

```:start.js
const base_url = 【RESTfulサーバのURL】;

var vue_options = {
    el: "#top",
    data: {
        target_url: '',
        shorter_url: ''
    },
    computed: {
    },
    methods: {
        make_shorturl: function(){
            var param = {
                targetUrl: this.target_url
            };

            this.shorter_url = '';
            do_post(base_url + '/urlshorter', param)
            .then( json =>{
                console.log(json);
                if( json.error ){
                    alert(json.error.message);
                    return;
                }
                this.shorter_url = json.shortLink;
            })
            .catch(error =>{
                alert(error);
            });
        }
    },
    mounted: function(){
    }
};
var vue = new Vue( vue_options );

function do_post(url, body){
    const headers = new Headers( { "Content-Type" : "application/json; charset=utf-8" } );
    
    return fetch(url, {
        method : 'POST',
        body : JSON.stringify(body),
        headers: headers
    })
    .then((response) => {
        return response.json();
    });
}
```

以下の部分を環境に合わせて修正してください。

【RESTfulサーバのURL】

# 実際に短縮URLを生成してみる

さていよいよ、短縮URLを作成しましょう。

その前に、ホワイトリストURLを登録する必要があります。
自由に短縮URLを作れるのではなく、ホワイトリストに指定したURLだけ、短縮URLが作成できます。ホワイトリストは、正規表現に似た記法でワイルドカードっぽく指定が可能です。

さきほどの、Dynamic Linksのページにおいて、「新しいダイナミックリンク」ボタンの右隣に、「・・・」があり、それを選択すると、「URLパターンのホワイトリスト登録」というメニューが表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/1b5c3ce5-a4b6-c6b6-5b32-e581608dd479.png)
 
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/a32e0a4d-b184-5232-1360-085b305964cc.png)

記載例は、詳細のリンク先を見ればだいたいわかります。難しくはないです。

さて、これで準備が整いました。
フロントWebページを開きます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/6c209922-c4ce-0654-91ae-7a18b54024cf.png)

targetUrlのテキストボックスに、短縮したいURLを指定します。また、URLに続けて?に続くパラメータを指定することが可能です。
短縮URLの生成ボタンを押下すると、めでたく、＜生成結果＞短縮URLのところに、以下のようなURLが生成されているのがわかります。

> https://【サブドメイン名】.page.link/XXXXXXXXXXXX

XXXXXXXXは、乱数であり、内容や長さは、入力したtargetUrlの内容や長さによって変わります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/a82209ca-e4b2-e550-51d8-02315eac5cf4.png)
 
ためしに生成された短縮URLをブラウザで開くと、targetUrlのページが表示されることがわかるかと思います。

以上です。