[symfony2.8] httpリクエストをコントローラで受ける前にmiddleware的なやつで正当性を判断したい

## 環境

- php7.0
- symfony2.8

## やりたいこと

slackの__outgoing webhook__から送られてくるリクエストのトークンが
正しいかどうかをコントローラでリクエストを受ける前に行いたい。

↓イメージ

```php:HogeController
/**
 * リクエストの正当性を事前に確かめておきたい
 * 
 * @param Request $request
 */
public function HogeAction(Request $request)
{
    /**
     * 正しいリクエストをサービスに投げて
     * あとはよしなにやるようにする
     */
    $this->get('service')->execute($request);
}
```


## どうやるのか

公式によるとやりたいことはこれなのかなと  

__How to Set Up Before and After Filters__
http://symfony.com/doc/2.8/event_dispatcher/before_after_filters.html

beforeフィルターを実装するつもりでやってみる。

## 手順

①バリデーションに使うtokenを設定
②チェックしたいコントローラにtokenチェックを行うかどうかを判別させるinterfaceを持たせる
__↑は他のいい方法ありそう__
③イベントリスナー作成
④作成したイベントリスナーの定義をymlファイルに書き込む

## 実践

### バリデーションに使うtokenを設定

githubの公開リポジトリ使ってるのでconfig.ymlには書きません

```php:parameters.yml
parameters:
    database_host: mysql
    .
    .
    .
    out_going_webhook_tokens:
        client1: xxxx
        client2: xxxx
```

### チェックしたいコントローラにtokenチェックを行うかどうかを判別させるinterfaceを持たせる

__interface作成__

```php:SlackWebhookTokenAuthenticated.php
<?php

namespace AppBundle\Controller\Support;

interface SlackWebhookTokenAuthenticated
{
    //空でok.
}
```

__Controllerに↑を持たせる__

```php:
<?php

namespace AppBundle\Controller;

use Symfony\Component\HttpFoundation\Request;
use AppBundle\Controller\Support\SlackWebhookTokenAuthenticated;

class HogeController implements SlackWebhookTokenAuthenticated
{

    /**
     * @param Request $request
     */
    public function HogeAction(Request $request)
    {
        //$this->get('service')->execute($request);
    }
}
```

### イベントリスナー作成

```php:SlackWebhookTokenListener.php
<?php

namespace AppBundle\EventListener;

use AppBundle\Controller\Support\SlackWebhookTokenAuthenticated;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;

class SlackWebhookTokenListener
{
    /**
     * @var array
     */
    private $outGoingWebhookTokens;

    /**
     * SlackWebhookTokenListener constructor.
     *
     * @param array $outGoingWebhookTokens
     */
    public function __construct(array $outGoingWebhookTokens)
    {
        $this->outGoingWebhookTokens = $outGoingWebhookTokens;
    }

    /**
     * @param FilterControllerEvent $event
     */
    public function onKernelController(FilterControllerEvent $event)
    {
        $controller = $event->getController();

        if (!is_array($controller)) {
            return;
        }

        if ($controller[0] instanceof SlackWebhookTokenAuthenticated) {
            $token = $event->getRequest()->query->get('token');
            if (!in_array($token, $this->outGoingWebhookTokens)) {
                throw new AccessDeniedHttpException('This action needs a valid token!');
            }
        }
    }
}
```

### 作成したイベントリスナーの定義をymlファイルに書き込む

```php:services.yml
services:
    app.slack_outgoing_webhook.action_listener:
        class: AppBundle\EventListener\SlackWebhookTokenListener
        arguments: ['%out_going_webhook_tokens%']
        tags:
            - { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
```

完成!!

あとはブラウザなりhttpクライアントツールでお試しください。。


## まとめ

コントローラに空のインターフェースを実装させてタグみたいに使うのは
初めてだったので新鮮だった。

今回の方法だとコントローラごとイベントリスナーの対象となってしまうので
メソッドごとにできたらいいなと。

laravelだとそこらへんが簡単にできるんだけどなぁ。。
routingに対してmiddlewareとかショートカット名とか
直観的に簡単に設定できるのが○

symfonyは開発者が知らなければいけない設定項目がファイルごとに違うので
割と手間だなぁ。。

そこんとこ、laravelはプログラミングコードベースで管理できるのでわかりやすい。

ただ、symfonyはlaravelよりも自由が利かないようになっているので
固く書きたいならいいなと。
あと、phpstormの補完が便利すぎて震える。