だるろぐ

明日できることは、今日しない。

WebMatrix で Markdown を使おう!

Markdown は軽量マークアップ言語で、 テキストを HTML へ変換する記法および変換ツール(パーサー)を指します。 Markdown の記法は英文メールでよく利用されるテキスト装飾がヒントになっており、変換元テキストから変換したあとの HTML マークアップの見当がつけやすく、覚えるのが比較的容易です*1。最近では GitHub などで Wiki 記法として用いられることが多くなっているので、覚えておいて損はないです。

この Markdown パーサーを「WebMatrix 2」で使ってみましょう(所要時間5分)。

準備

まず、 Empty Site テンプレートから新規サイトを作成し*2、適当にフォルダーを作って Markdown テキストのサンプルを配置します。

f:id:daruyanagi:20120816030739p:plain

今回、サンプルは http://tkns.homelinux.net/modules/manual/ja/data/markdown-sample.text (テキスト形式、UTF-8エンコード)をお借りしました。ちなみに、 “.md” や “.markdown” といった拡張子*3のファイルを「WebMatrix 2」で開くには、コンテキストメニューから[WebMatrix で開く]を選択します。

つぎに、パーサーをインストール。 NuGet で「MarkdownSharp」パッケージを選択しましょう。

f:id:daruyanagi:20120816031246p:plain

C# の Markdown エンジン - だるろぐ にはいくつかあるのですが、「MarkdownSharp」がもっとも無難だと思います。

f:id:daruyanagi:20120816031256p:plain

インストールはとっても簡単。勝手にレポジトリからダウンロード・セットアップされます。ほかのパッケージが必要であれば*4、そのパッケージも自動でセットアップされます。パッケージは自分でも作れるので、やってみると面白いですネ。

これで準備は完了。

コーディング

Default.cshtml を開いて、以下のように記述します。 Markdown テキストのサンプルへのパスは適当に環境に合わせて変えてください*5

@{
    var markdown = new MarkdownSharp.Markdown();
    var path = Server.MapPath("~/App_Documents/Sample.markdown");
    var encoding = System.Text.Encoding.UTF8;
    var text = System.IO.File.ReadAllText(path, encoding);
}

<!DOCTYPE html>

<html lang="ja">
    <head>
        <meta charset="utf-8" />
        <title>マイ サイトのタイトル</title>
    </head>
    <body>
        @Html.Raw(markdown.Transform(text)) <!-- Add this ! -->
    </body>
</html>

これで終わり! あとは[実行]ボタンを押せば Markdown テキストが HTML へ変換されて表示されるはずです。

f:id:daruyanagi:20120816032025p:plain

new MarkdownSharp.Markdown() でパーサーのインスタンスを生成し、 Transform() で変換します。ただし、結果をそのまま利用しても HTML エンコードされた状態で表示されてしまう*6ので、 Html.Raw() でそれを抑止します。 Server.MapPath() を利用すれば、ディスク上の物理パスを取得できます。ファイルを扱うときはよく利用するので覚えておくといいかも。あとは .NET Framework ではおなじみの処理ですね。

おしまい!

ステップアップ

ルーティング

これだけだと面白くないので、ルーティングを利用してみました。

# Page.cshtml

@{
    var markdown = new MarkdownSharp.Markdown();

    var path = Server.MapPath(
        string.Format("{0}{1}.markdown",
            "~/App_Documents/",
            UrlData.Count == 0
                ? "Default"
                : string.Join("/", UrlData)
    ));

    var encoding = System.Text.Encoding.UTF8;
    var text = System.IO.File.ReadAllText(path, encoding);
}

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        @Html.Raw(markdown.Transform(text))
    </body>
</html>

これで ~/Page/サンプル/Sample などというパスも扱えます。 ~/Page.cshtml で受け取った UrlData には ["サンプル", "Sample"] という配列が格納されているので、それを適当に加工してください(WebMatrix のルーティング - だるろぐ も参考にしてみてください)。

f:id:daruyanagi:20120816035052p:plain

もう少しキレイに

ページを表示するごとに MarkdownSharp.Markdown を new したり、 @Html.Raw( markdown.Transform(text) ) を呼び出すのはあまりかっこうがよくない気がしますね。

そこで試しに、 ~/App_Code/Markdown.cs というコードを書いてみました。

using System;
using System.IO;
using System.Text;
using System.Web;

/// <summary>
/// Summary description for ClassName
/// </summary>
public static class Markdown
{
    private static readonly MarkdownSharp.Markdown md =
        new MarkdownSharp.Markdown();
    private static readonly Encoding encoding = Encoding.UTF8;

    public static HtmlString Load(string path)
    {
        return new HtmlString(
            md.Transform(
                File.ReadAllText(path, encoding)
            )
        );
    }
}

これで Page.cshtml がちょっとシンプルになりました。

@{
    var path = Server.MapPath(
        string.Format("{0}/{1}.markdown",
            "~/App_Documents/",
            UrlData.Count == 0
                ? "Default"
                : string.Join("/", UrlData)
    ));
}

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        @Markdown.Load(path)
    </body>
</html>

HtmlString 型で返せば、ビューで @Html.Raw() が要りません。あとは、 Url Rewrite を使って /Page/ なしで利用できるようにするとか、 ファイルの作成日時から RSS を吐いてみるとか(キャッシュを使わないとエラいことになりそうですね!)まだまだ改善の余地がありそう。

*1:弱点は若干機能不足なこと。これを補う派生版が幾つかあります

*2:簡便のため。好きなテンプレートを利用してもよい

*3:べつにこれである必要はないが、 Markdown 書式のテキストであることを示すためにたびたび用いられる

*4:依存関係があるといいます

*5:ほんとは App_Data フォルダに Markdown テキストを格納したかったのだけれど、なんかいろいろファイルが入っていてヤダ

*6:セキュリティ上の理由です