update フィルタと validation のコンビネーションでHTTPを制御する。

http://d.hatena.ne.jp/yssk22/20091212/1260617233 に書いたupdateフィルターですが、こんなことをしたくなるかもしれません。

// test/updates/foo.js 
function(doc, req){
   var v = parseInt(req.form.value)
   if( v > 0 ){
      doc.value = v;
      return [doc, "OK"];
   }else{
      return [null, {code: 403, body: "forbidden"}]
   }
}

つまり、予期しないパラメーターが渡されたらドキュメントの更新をやめて(returnの第一要素がnullの場合は、何もしません)、4XX のものを返したくなるでしょう。403よりも400のほうがよさそうですが、それはおいといて、updateフィルター関数内ではHTTPのステータスコードを制御できないようになっています。

この場合は、validate_doc_update.js を使っておきます。

// test/updates/foo.js 
function(doc, req){
   doc.v = parseInt(req.form.value)
   return [doc, "OK"];
}

// test/validate_doc_update.js
function(newDoc, oldDoc, userCtx){
   if(!(newDoc.value > 0)){
      throw({forbidden: "forbidden"})
   }
}

こうやっておくと、updates/foo/docId をformを使ってvalue=-1などでたたいたときには、ちゃんと 403 がかえるようになります。つまり、updateフィルタを通過したドキュメントがvalidateにかけられてそこで例外が投げられると、レスポンスを上書きできる(この場合 "OK" => "forbidden" にして 200 -> 403 にする)ことになります。

大抵はこうやって、validateはvalidate_doc_update.js に。updateフィルタでは検証せずとりあえず値をセットしておく、ということになります。

これはこれでいいのですが、たまに不便で、updateフィルターで、不整合が起こりそうだったら更新そのものを中止することを明示的に書いておきたい場合があります。

// test/updates/foo.js 
function(doc, req){
    // なんか不整合が起こった
    throw("error");
}

こんな形でかけるといいのですが、あいにく、update フィルタ内の例外は厳禁です。500 になるだけです。なので、"エラードキュメント"を返してやることで、validate_doc_update.js と連携するとよいです。

// test/updates/foo.js 
function(doc, req){
    try{
       // いろいろやる
       // 例外を投げる
       throw({forbidden: "forbidden"});
    }catch(e){
       // 例外を拾って、UpdateErrorDoc という架空のドキュメントタイプでvalidate_doc_update に伝える
       return [{type: "UpdateErrorDoc", error: e}, "ここは使わない];
    }
}

// test/validate_doc_update.js
function(newDoc, oldDoc, userCtx){
   // UpdateErrorDoc は update フィルタから送られてきた例外検知ドキュメントなので、有無をいわさずにthrowする。
   if( newDoc.type == "UpdateErrorDoc"){ 
      throw(newDoc.error) 
   }
}

こうすると、updateフィルタ内でも4XXや3XXなどのステータスコードを返す処理もかけるようになります。update フィルタはHTTPを扱うものの、実質はMVCでいうところのMでしかなくて、エラーになる場合はモデルの処理でエラーオブジェクト(ドキュメント)を返すだけにすることで、C相当のvalidate_doc_updateで正しくHTTPを操作してあげるのが良さそうです。

なんか、とっつきづらい気もしますが、これはこれで意外と便利だったりします。