2009年3月1日日曜日
Crystalized Intelligence
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>
リンクをクリックする。
ちゃんと犬が表示されました。一応、ブレークポイントセットして変数の中身を確認。ちょと見にくいけど、引数のbase64UrlにはBase64エンコードされたBinary型の値が入ってて、変数urlには元のURLがデコードされてる。
問題はぱっと見どこのURLを参照してるのかを判断出来ないところ。フレンドリURLとは言い難し。
ところで、Windows Live Writerから投稿すると、毎回無駄な改行が入るのはなんでなんだろう。
2009年2月26日木曜日
ModelBinderでLINQ to SQLのモデルをそのまま使う
ASP.NET MVC Tip #49 - Use the LinqBinaryModelBinder in your Edit Actions
ステファン君それは無理じゃない?更新以前にバインドした時点で例外でるじゃん。
と、思ってたのは今は昔。RCでは何の問題もなくバインド出来るみたい。試してみたら、普通に出来た。ずいぶん前に試したときはデータベースコンテキストがスタティックじゃないと例外でたり(ModelBinderに気をつけねば)、そもそも直接DBO使うのはうんぬんかんぬん。綺麗な設計どうのじゃなくて、純粋に少ないコード量でどこまで出来るか、っていうのを考えたら直接使う事もあったりするのかもね。
これまでも使ってたサンプルはDBを使ってなかったので、改めてDBを用意して、LINQ to SQLのモデルを作成。
これを利用するためのコントローラも定義。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Mvc.Ajax; using Mvc.RC.Models; namespace Mvc.RC.Controllers { public class OnePeaceController : Controller { OnePeaceDataContext _context = null; public OnePeaceController() { _context = new OnePeaceDataContext(); } public ActionResult Index() { return List(); } public ActionResult List() { var people = _context.Persons; return View("List", people); } public ActionResult Details(int id) { var people = _context.Persons; return View(people.First(p => p.Id == id)); } [AcceptVerbs(HttpVerbs.Get)] public ActionResult Edit(int id) { var people = _context.Persons; return View(people.First(p => p.Id == id)); } [AcceptVerbs(HttpVerbs.Post), ValidateInput(false)] public ActionResult Edit(int id, Person person) { try { _context.Persons.Attach(person, true); _context.SubmitChanges(); return RedirectToAction("Index"); } catch (Exception ex) { return View(person); } } } }
List/Detail/Editの各ViewはRCで追加されたAdd Viewコマンドでサラッと作成。こういう時にとても便利ですね。Attachのところでブレークポイントをセットして、まずはこのまま動かしてみたところ、DB使っててもホントに動く。信じてなかったわけじゃないけど、こうもあっさりと動くとちょっと感動する。
↑Listページ。
↑Editページ。
↑普通にバインドされてる様子。
でも、このままステップ実行すると、確かに例外が発生。
エンティティは、バージョン メンバを宣言するか、または更新チェック ポリシーを 含まない場合にのみ、元の状態なしに変更したものをアタッチできます。
System.Exception {System.InvalidOperationException}
と、言うことで、簡単に更新チェックポリシーをオフにしてしまおうかとも思ったけど、それよりちゃんと競合チェックするようにtimestamp型の列をテーブルに追加して動かしてみます。
↑ChangeStampという名前のtimestamp型の列を追加したモデル。
でも、timestamp型はSystem.Data.Linq.Binary型としてクラスが生成されるので、このままだとちゃんとモデルが復元されません。なによりViewにちゃんと出力してないし。まずはEditのViewにChangeStampをhiddenで展開するコードを追加。
<p> <%= Html.Hidden("ChangeStamp", Convert.ToBase64String(Model.ChangeStamp.ToArray())) %> <input type="submit" value="Save" /> </p> </fieldset> <% } %> <div> <%=Html.ActionLink("Back to List", "Index") %> </div> </asp:Content>
submitの手前に入れてます。Base64エンコードしてHiddenフィールドに。このままだとDefaultModelBinderが復元してくれくれないので、Global.asaxのApplication_Start時にASP.NET MVC Futuresに入ってるLinqBinaryModelBinderを登録。
protected void Application_Start() { RegisterRoutes(RouteTable.Routes); ModelBinders.Binders.Add(typeof(System.Data.Linq.Binary), new LinqBinaryModelBinder()); }
もう、何もかもステファンさんのいいなりです。
この状態で動かしてみて、Viewが出力するHTMLソースのChangeStamp部分を確認してみたのが↓これ。
<input id="ChangeStamp" name="ChangeStamp" type="hidden" value="AAAAAAAAB9Q=" />
ちゃんと、エンコードされて出力されてます。あたりまえだっちゅーの。
これを更新するためにsubmitして、ブレークポイントでモデルの中身を確認。ちゃんとLinqBinaryModelBinderでBinary型も復元されてます。そのまま実行を続けても例外は出なくて、テーブルも更新されてました。で、更新された後のViewのCangeStampを確認してみる。
<input id="ChangeStamp" name="ChangeStamp" type="hidden" value="AAAAAAAAB9U=" />
ちゃんと、最初とは違う値が入ってますね~。
と、いうことで、ステファンさんのやったことをそのまま試したみたわけですが、これが出来るって事はLINQ to SQL+スキャッフォールディングで凄く簡単にDBを使ったアプリケーションを作成出来る事になりますね。Repositoryは作るにしても、ViewModelを作らずシンプルなコードで開発することが出来るのでWebForms並の生産性(ViewStateとデータバインディング)を実現できてるんじゃないかと思う次第です。
※ViewStateはModelStateが各inputフィールドの値を保持しつつHTMLにレンダリングしてくれたりするので。
2009年2月25日水曜日
Bloggerをいじる
とりあえずは、コードをエントリーに書くことが多くなるとは思うので、綺麗に見せるためにGoogle Code Prettifyを導入してみる。
google-code-prettify - Google Code
ここからコードをダウンロードできるんだけど、はて、このJSファイルとCSSファイルはどこにアップロードするんでしょうね。困りますね。面倒なのでずるいとは思いつつ、SVN-Tranc(google-code-prettify - Revision 64: /trunk/src)から直接貼り付けちゃった。てへ。
テンプレートを編集する画面でheadタグ内の<title>の下にダイレクト挿入!
<script src='http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js' type='text/javascript'/> <link href='http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css' rel='stylesheet' type='text/css'/>
後は、コードを載せたい箇所を<pre class=”prettyprint”></pre>で囲むだけ! とは言ってもですね、毎回ソースを開いてそんなことやってられないじゃないか! 便利な物はないかな~、と思って行き着いたのが「Windows Live Writer用のプラグインを開発する:CodeZine」。
おっと、いきなりプラグイン開発ですか?そうですか。それもやむなしですね。どれどれ、記事を読んでみると大して難しいことしなくてもこの程度のことならサクッと作れそう。だってpreで囲むだけだし。SmartContentSourceじゃなくてContentSourceで作れるじゃんね。んで、作ったDLLをPluginフォルダに入れとけばイイみたいだし。良し作るか。と、思ってちなみに同じ事考えてる人いるんじゃないかと思って、改めて検索し直してみたら板いた。Vistaのサイドバーガジェットの時の無駄作業を思い出して良かった。あのときも同じようなの作ってる人いたもんね。ちゃんと調べないで作っちゃって損した気分になったし。
Code Prettify for Windows Live Writer – Home
コード見たらビックリだね。こんなに潔いプラグインも珍しい。いや、だって、自分でもきっとこう書く。
次にデフォルトのコメントシステムはいきなりスパムが来たりするくらいなので、速攻で消してとりあえずFriendConnectにしてみたんだけど、TumblrでTypePad Connectを使ってるの思い出した。
Bloggerもサポートしてるよ!と豪語してるだけあって、設置方法もスクリーンショットでテンプレート編集箇所を書いてあったりして、親切だ。
でも、動かなかった。 だいたい、書いてあるとおりにやってみたけどダメだった。Bloggerのテンプレートがどうなってる(構造が)のか調べるのも面倒だしな~。このブログに適用してるブログにはそもそもインストール手順に書いてるような項目もないし。インストール手順がデフォルトテンプレートを基準にするのは、そりゃあたりまえだしね。違うデザインのテンプレートを使ってるんだから致し方なし。
かといって、諦めるのはダンディじゃない。 無理矢理にでも動かしてやる。大まかな手順は3つ。
1.コメントフォームとコメント一覧を表示する部分のコードを貼り付け。 2.コメント件数を表示するアンカーを貼り付け。 3.コメント件数を表示するスクリプトを貼り付け。
1と3の作業は手順書通りに出来そう。ちゃんと該当箇所も見つかるし。 2の作業は...。今度は手順書の場所が見あたらない。なんか、ちょっとテンプレートが違う。
でも、まぁ、同じようなモンだろう、ってことで、↓こんな感じで貼り付けてみた。
<b:if cond='data:post.allowComments'> <h4> <b:if cond='data:post.numComments == 1'> 1 <data:commentLabel/>: <b:else/> <data:post.numComments/> <data:commentLabelPlural/>: </b:if> </h4> <b:else/> <a expr:href='data:post.url + "#comments"'>Comments</a>
で、動かしてもこれがちゃんと表示してくれないんだよね~。 上のコードのリンクは表示されてるんだけど、入力フォームが出てこない。こりゃどうしたものか。 出てこないってことは、貼り付ける場所を間違えてるんだろう、と。 絶対出てくるところに貼り付ければいいじゃん。 ってことで、上記Commentsリンクの直下に貼り付けてみた。
<b:if cond='data:post.allowComments'> <h4> <b:if cond='data:post.numComments == 1'> 1 <data:commentLabel/>: <b:else/> <data:post.numComments/> <data:commentLabelPlural/>: </b:if> </h4> <b:else/> <a expr:href='data:post.url + "#comments"'>Comments</a> <!-- START TypePad Connect --> <b:else/> <div class="comments-content"> ~ここに手順書のコード~ </div> <!-- END TypePad Connect -->
これで、どうかな~。うぬ。ちゃんと出るね。
ということで、Friend Connectは削除して、コメントシステムはTypePad Connectで一本化です! スッキリした。
LINQで今日以降今年いっぱいの日曜の日付を表示
LINQ練習 #1 「LINQ to Object 基本」 - 悠希 - builder by ZDNet Japan
ここで書かれてるコードが↓これ。
List<DateTime> list; list = new List<DateTime>(); for(int i=0;i<=365;i++) list.Add(DateTime.Today.AddDays(i)); int nowYear; nowYear = DateTime.Today.Year; var sunday = from M in list where M.DayOfWeek == DayOfWeek.Sunday && M.Year = nowYear select M; foreach (var row in sunday) { Console.WriteLine(row.ToShortDateString()); }※今年かどうか判定する条件の部分が代入になってるよ~。
せっかくLINQを使うっていうお題なんだから日付の生成部分もList<DateTime>に生成して入れておく、なんてことをしないで、そこもLINQに含めちゃった方がオシャレ感でると思うよ~。そう、例えば↓こんな感じでね。
var today = DateTime.Today; var sunday = from date in from day in Enumerable.Range(0, 365) select today.AddDays(day) where date.DayOfWeek == DayOfWeek.Sunday && date.Year == today.Year select date;
2009年2月24日火曜日
心機一転
BloggerのRate Limit
400 Bad Request Blog has exceeded rate limit or otherwise requires word verification for new postsと、悲しいメッセージが出てくる。 50件あたりで出てきた気がする...。切ないね~。 ブラウザ経由でも、API経由でも出るみたいだから、一気にやるならImport機能のフォーマット似合わせたXMLファイルを作成して、それを取り込むのがいいんだろうね。 面倒くさいな~。特に、画像をPicasaに入れてimgタグのsrc書き換えとかゲンナリするし。 まぁ、ASP.NET MVC関係のエントリだけを移行させようと思ってるから手作業でやるさ。
dotnetConf2015 Japan
https://github.com/takepara/MvcVpl ↑こちらにいろいろ置いときました。 参加してくださった方々の温かい対応に感謝感謝です。
-
Working with SSL at Development Time is easier with IISExpress - Scott Hanselman Hanselmanさんのエントリに書かれてる通りデス! IIS ExpressでSSLを有効にしたデバッグだと無効...
-
How to iterate through objects in ViewData via javascript on the page/view? - Stack Overflow この質問の回答としては、ページにscriptタグを書き、その中でJavaScriptを書き...
-
すでにコロケーションサービスを利用したりして、データセンターには自前のサーバーがいて。でも、今のデータセンターの利用を拡張していったり、するのもどうかなー、資産化するのもなー。という時にはパブリッククラウドにVPN接続させて、オンプレミスとのハイブリッド。 ハイブリッドクラウド...