SIer だけど技術やりたいブログ

jQueryのajaxメソッドでHTTPリダイレクト(ステータスコード302)を受け取った時は、リダイレクト先の結果しかdone(),fail()メソッドに渡せない

javascript SpringSecurity

springsecurity使うときにこの挙動がわかってなくて困ったのでメモ。タイトル長っ。

どんな内容?

jQueryのajax関数内でリスエストを送った結果がHTTPリダイレクト(ステータスコード 302) だった場合、jQueryのdone(),fail()メソッドがコールされる前にブラウザ側で勝手にリダイレクトされ、リダイレクトした結果がdone(),fail()に渡される。

どういうこと?

下記のコードを実行すると、done()メソッドにリダイレクト先のレスポンスであるHTMLがテキストデータとして渡される。

$.ajax({
    type: "GET",

}).done(function (data, textStatus, jqXHR) {
    console.log(data);
}).fail(function (jqXHR, textStatus, errorThrown){
    console.log(jqXHR, textStatus, errorThrown);
});

consoleに出力される実行結果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>HELLO</title>
</head>
<body>
    <h1>HELLO WORLD</h1>
</body>
</html>

サンプル書いた。
https://github.com/kimullaa/why-jquery-redirect-why

なんで?

jQueryに限った話ではなく、jQueryが内部で利用しているXMLHttpRequest自体がそういう挙動らしい。また、javascript側でリダイレクトしないようにブラウザの挙動を制御する方法もないっぽい。
stackoverflowだけどこれがけっこう参考になった。

自分が調査してるときは「JQuery 302」といったキーワードでググっていたのであまりヒットしなかったが「XMLHttpRequest 302」でググると結構ヒットした。

SpringSecurityでどういうときに困るの?

springsecurityでセッションタイムアウトした時にリダイレクトするURLの指定を、ajaxと画面遷移で一緒にしているときに困る。こんな感じ。詳細はここ参照。

<sec:http auto-config="true">
  <sec:session-management invalid-session-url="/" />
</sec:http>

解決方法

  • クライアント側でリクエストを送るときにdataTypeにjson指定するなりして、リダイレクト先のHTMLが返ってきたときにfail()が呼ばれるようにする。その上で、共通的な例外処理をして終わりにする。
  • redirect先のcontrollerでリダイレクト結果をHTML返すパターンとJSON返すパターンで振り分ける。JQueryのリクエストヘッダーには「X-Requested-With : XMLHttpRequest」がつくのでそれで判別する。
  • セッションタイムアウト時のリダイレクト先を変える。ここが参考になる。

教訓

  • ライブラリ使うときは、その基盤に何を使ってるのか理解しといたほうが、障害解析やらで役に立つ。(あたりまえ)