このブログははてなブログからの移行記事です。

環境

ajax通信にsuperagentを使用したい

JS弱者なりに「jQueryから自立したい…!」と感じ、Ajax通信を$.ajaxでなくsuperagentというものを採用しました。

かのexpressやstylusを開発したTJ作ということもあり、とても使いやすい!

import request from 'supreagent';

request.get('/api/hoge')
  .query({ num: 5 })
  .end((err, res) => {
    if (err) console.error(err);
    console.log(res.body);
  });

ただ、コイツをLaravel5.1で使った時にハマりました。

Request::ajax()の罠

LaravelではHTTPリクエストが¥Illuminate¥Http¥Requestインスタンスとしてアプリケーションに渡されます。

また、その内容であらかじめ処理を分けるためにミドルウェアを使用することができます。

その中で「Ajax通信の場合、エラーメッセージを返す」といった処理をしたい場合は下記のようなミドルウェアを書くことで実現できます。

// 前後略

    /**
     * @param ¥Illuminate¥Http¥Request  $request
     * @param ¥Clouser  $next
     * @return mixed
     */
    public function handle($request, Clouser $next)
    {
        // リクエストがAjaxの場合、エラーメッセージを返す
        if ($request->ajax()) {
            return response()->json([ message => 'Server error, ajax is not allowed...' ]);
        }
        
        return $next($request);
    }

このajax()部分で少しハマりました。

superagentのリクエストはajax()に引っかからない

このajax()関数を使用してsuperagentのAjaxリクエストを判別しようとしたところ、どうやっても$request->ajax()falseを返していました。

どういうことだと思いコードを読むと\Symfony\Component\HttpFoundation\RequestisXmlHttpRequest()を使用していることが分かりました。

ここだけ抜粋すると以下の処理を行っています。

    public function isXmlHttpRequest()
    {
        return 'XMLHttpRequest' == $this->headers->get('X-Requested-With');
    }

X-Requested-Withヘッダーの中身を見て真偽値を返しているだけですね。

つまりはこのヘッダーが無いとajax()がきちんと動きません。

ここでおもむろにsuperagenthistory.mdを読んでみます。

そう、3年も前に廃止されています。

該当のIssueを見てみると「jqueryのヘッダだしセンスないよ」とだけTJが言ってます。

remove x-requested-with · Issue #189 · visionmedia/superagent · GitHub

このヘッダの出処をきちんと調べられてないですが、いろいろ調べてみるとこのヘッダがHTTP公式のものでなく独自ヘッダであることは確かなようです。

解決策

とはいえ、独自ヘッダだから使わないというわけにもいきません。

解決策はいくらでもあると思いますが、ヘッダが必要なら足せばよいのです。

私の場合、以下の様なsuperagent用ヘルパーメソッドを1つ用意し、それを全てのリクエストに噛ませるようにしています。

(ついでにCSRFトークンも渡せるようにしてたりします。)

/**
 * @description Laravelに送信するためのヘッダーを付与したsuperagentを返す
 * @method addLaravelHeader
 * @param {object} request - superagent object
 * @param {string} csrfToken
 * @return {object} superagent
 */
export function addLaravelHeader(request, csrfToken) {
  return request
    .set('X-CSRF-TOKEN', csrfToken)
    .set('X-Requested-With', 'XMLHttpRequest');
}

こうすることで$request->ajax()した時にAjaxリクエストだと認識してくれるようになります。

使い方は以下のとおり。ファイル名はよしなに。

import request from 'superagent';
import { addLaravelHeader } from './superagent-helper';

let csrfToken = 'csrf-token'; // CSRFトークンをよしなに取得
addLaravelHeader(request.get('/api/hoge'), csrfToken)
  .query({ num: 3 })
  .end((err, res) => {
    // 煮るなり焼くなり
  });

以上!