nodeでガラケー対応したメモ

Webサービス的なものをnodeでつくってて、ガラケー対応しないといけないことはわかってたんだけど、しばらくガラケー対応とかやってなかったので、すっかり文字コードのことを忘れてて痛い目みた。

ガラケーだけPHPPerlで書こうと思ったけど、そんなに大きいアプリじゃないのでできればPCとガラケーのロジック同じにしたかったので頑張ってみた。

入力は全部SJISからUTF-8に変換して、出力はUTF-8からSJISに変換する。内部では全てUTF-8としてデータを扱う。一部のSoftBank端末がSJISで問題あるからUTF-8がいいとかはこの際気にしない。

まず端末を判定するところ。

app.use(function(req, res, next) {
  var ua = req.headers['user-agent'];
  var regexp = /^(DoCoMo|KDDI|Up\.Browser|J-PHONE|vodafone|SoftBank)/i;
  req.isMobile = regexp.test(ua);
  req.encoding = req.isMobile ? 'shift_jis' : 'utf8';
  next();
});

まあ判定ロジックは手抜きにしたので簡単。

次に出力をSJISにする。これはrenderするところでiconv使ってutf8をshift_jisに変換する。

var Iconv  = require('iconv').Iconv;
var iconv = new Iconv('utf-8', 'shift_jis');
...
if (req.isMobile) {
  // ガラケーの時の処理
  res.render('m/' + tmpl, data, function(err, html) {
    res.header('Content-Type', 'text/html; charset=shift_jis');
    res.send(iconv.convert(html));
  });
}
else {
  // 普通のPCの処理
  res.render(tmpl, data);
}

(iconvのインスタンスつくるのはコールバックの中のほうがいいかもとのこと。コメント欄参照)
こんな感じ。expressの第三引数にコールバックを指定するとrender結果を受け取れるということがわかったので、割とキレイに実装できた。

問題は入力。POSTで受け取ったリクエストbodyをパースするところで、connectのbodyParserはdecodeURIComponentを使っていて、これがSJISに対応してないのでSJISでPOSTするとエラる。

これはブラウザのJavaScriptで同じ問題らしく、SJISのパーセントエンコーディングをデコードしてくれるやつがあった。
http://travel.han-be.com/ecl/Escape%20Codec%20Library%20ecl_js.htm
ecl.js (Escape Codec Library) と Sleipnir の相性が悪い件について - drk7jp

これでSJISのデコードができるので、decodeURIComponentを使ってたところで

var decoder = encoding === 'shift_jis' ? unescapeSJIS : decodeURIComponent;

とかして、エンコーディングを渡してshift_jisだったらelc.jsのやつを使ってデコードするとかした。手を入れたのはconnect/middleware/bodyParser と qs。

これで何とか対応できたけどまあとりあえず今後はガラケー対応はnodeじゃやらない。