2009年2月28日土曜日

Base64でエンコード

Url encoded slash in URL - Stack Overflow

この投稿から始まって、以下のエントリにたどり着く。

Allowing special characters (forward slash, hash, asterisk etc) in ASP.Net MVC URL parameters

まさに、同じ問題に直面した内容。

Double/incomplete Parameter Url Encoding - Stack Overflow

↑ここへのリンクもあったから覗いてみたら、Uri.EscapeDataString メソッド (System)を使ったエンコーディングがあるということを初めて知る。なにこれ~、と思って試したけど、Server.UrlEncodeと同じだった。ガッカリ。いや、違う方が驚きか。

結局、Philさんの投稿のように"/"か他の文字に置換して区切ってしまうか、Base64エンコードで渡すのがいいのかな。Base64といえばFuturesに入ってるLinqBinaryModelBinderが使えるんだもんね。

というわけで試してみた。

登録するルートは以下の通り。

routes.MapRoute(null,
"Proxy/{*base64url}", 
new { controller = "Images", action = "Proxy" } 
); 

Controllerはシンプルに以下。LinqBinaryModelBinderを使うので、参照設定にFuturesのアセンブリを含めるのを忘れずに(単純に文字列で渡しておいて、Binary型にしなくてもConvert.FromBase64Stringでも可)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.Net;
using System.Text;
using System.Data.Linq;

namespace Mvc.RC.Controllers
{
 public class ImagesController : Controller
 {
   public ActionResult Proxy(Binary base64url)
   {
     var web = new WebClient();
     var url = Encoding.UTF8.GetString(base64url.ToArray());

     var bytes = web.DownloadData(url);
     return File(bytes, web.ResponseHeaders["Content-Type"]);
   }
 }
}

Viewでリンクを作る時にBase64エンコードしたパラメータを渡す。
  <% = Html.ActionLink("いぬ", "Proxy", "Images", new {
 base64url = Convert.ToBase64String(Encoding.UTF8.GetBytes(
         @"http://farm1.static.flickr.com/131/353753310_1ed04f694c_m.jpg"
         ))
 }, null)%>

これが出力されると以下のHTML。

<a href="/Proxy/aHR0cDovL2Zhcm0xLnN0YXRpYy5mbGlja3IuY29tLzEzMS8zNTM3NTMzMTBfMWVkMDRmNjk0Y19tLmpwZw==">いぬ</a>

リンクをクリックする。

base64

ちゃんと犬が表示されました。一応、ブレークポイントセットして変数の中身を確認。

base64_2

ちょと見にくいけど、引数のbase64UrlにはBase64エンコードされたBinary型の値が入ってて、変数urlには元のURLがデコードされてる。

問題はぱっと見どこのURLを参照してるのかを判断出来ないところ。フレンドリURLとは言い難し。

ところで、Windows Live Writerから投稿すると、毎回無駄な改行が入るのはなんでなんだろう。