問題
社内のシステムが独自ヘッダをつけてリクエストを投げてくるのですが、名前にアンダースコアが含まれている(仮にfoo_barとします)のでLaravelの普通のやり方$request->headers->get('foo_bar')では取得できませんでした。
調査
最初は「_」が問題とはわかってなくて、まず切り分けということでいろいろ調べたらこんな感じでした。
| 対象 | foo_barヘッダの有無 |
|---|---|
| WireSharkでパケットダンプ | ある |
$_SERVER |
ない |
getallheaders() |
ある |
$request->headers |
ない |
Laravelはヘッダの取得はSymfonyの\Symfony\Component\HttpFoundation\ServerBagクラスのgetHeaders()で行っていて、これは$_SERVERから取得するようになっていたので、$_SERVERに含まれていないのが問題のようです。
また試しに別の名前のヘッダを追加したりしてみたらどうやら「_」が含まれてるとダメらしいとわかってきました。
そこで「ヘッダ名」「アンダースコア」で検索してみると、HTTPヘッダ名にアンダースコアはダメよ。が見つかりました。apacheがやっているということのようです。(確かにapache+mod_php環境でした)
とはいえ、いまさら社内システム側を変えてくれという訳にもいかず、getallheaders()ではとれるからそれでなんとかする方針にしました。
対策
$request->headers->get()しているところをgetallheaders()を使うように書き換えればとりあえず動きはするのですが、phpunitでのテストがやりにくいのでひねり出したのが以下のミドルウェアを使う方法です。
getallheaders()でfoo_barヘッダがみつかったら、追加してやるという仕組みです。これをグローバルミドルウェアとして登録しました。
class FooBarHeader
{
private const HEADER_NAME = 'foo_bar';
public function handle(Request $request, \Closure $next)
{
if (function_exists('getallheaders')) {
$headers = \getallheaders();
if (isset($headers[self::HEADER_NAME])) {
$request->headers->add([self::HEADER_NAME => $headers[self::HEADER_NAME]]);
}
}
return $next($request);
}
}
いったん追加してしまえば通常のやり方で取得できるようになるし、通常のやり方で書いてあればテストのときに$this->withHeaders()が使えます。
おわりに
ヘッダ名には英数字と「-」以外の文字は使わないようにしましょう。