httpまたはhttpsでのアクセスを強制するカスタムフィルター

httpsでしかアクセスさせたくないページの場合、そのページのアクションメソッドに [RequireHttps]属性を付けると、httpでアクセスしたとき自動時にhttpsにリダイレクトさせることができます。

でもその逆、httpsでアクセスしたときにhttpにリダイレクトさせるような属性は用意されていません。なので自分で作る必要があります。

これ既に作っていた人がいて、それがこちらの記事。消えちゃってるのでInternetArchiveとGistの直リンク。

このカスタムフィルター属性結構良いと思うのですが、2点おしいところがあります。

  1. SSLSSLページで異なるドメイン*1やポートを使用している場合対応できない*2
  2. クエリストリングやアンカー(#)を引き継いでリダイレクトできない*3

1は開発サイトとかでありがち。

その辺を考慮して少し修正した対応版作りました。

using System.Web.Mvc;
 
namespace Utils.Filter
{
    public enum Scheme
    {
        Ignore,
        Http,
        Https,
    }
 
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
    public class RequireHttpSchemeAttribute
        : FilterAttribute, IAuthorizationFilter
    {
        private readonly Scheme scheme;
 
        public RequireHttpSchemeAttribute(Scheme scheme)
        {
            this.scheme = scheme;
        }
 
        public void OnAuthorization(AuthorizationContext filterContext)
        {
            if ((scheme == Scheme.Https) && !filterContext.HttpContext.Request.IsSecureConnection)
            {
                filterContext.Result = GetResult(filterContext, "https://localhost:1443");
            }
            else if ((scheme == Scheme.Http) && filterContext.HttpContext.Request.IsSecureConnection)
            {
                filterContext.Result = GetResult(filterContext, "http://localhost:1180");
            }
        }
 
        private static RedirectResult GetResult(AuthorizationContext filterContext, string baseUrl)
        {
            var uri = new Uri(baseUrl);
 
            return new RedirectResult(uri.GetLeftPart(UriPartial.Authority) + filterContext.HttpContext.Request.RawUrl);
        }
    }
}

URLベタ書きの部分は普通はWeb.configとかに記述してあると思うからそれを使いましょう。

使い方

こんな感じです。

[RequireHttpScheme(Scheme.Http)]
public virtual ActionResult HttpOnly()
{
    return View();
}

これはhttpsでアクセスされた場合、httpにリダイレクトする。

[RequireHttpScheme(Scheme.Https)]
public virtual ActionResult HttpsOnly()
{
    return View();
}

これはhttpでアクセスされた場合、httpsにリダイレクトする。

[RequireHttpScheme(Scheme.Https)]
public class AlmostSecureController : Controller
{
    public virtual ActionResult HttpsOnly()
    {
        return View();
    }

    [RequireHttpScheme(Scheme.Ignore)]
    public virtual ActionResult DoesntMatter()
    {
        return View();
    }
}

これはコントローラー自体に属性が付いていますので、このコントローラー内の全てのアクションメソッドでhttpsでのアクセスを強制しようとしています。しかし例外として特定のアクションメソッドにはリダイレクト処理をさせたくないという場合があります。
そういうときに例外にしたいアクションメソッドにScheme.Ignoreを設定したRequireHttpScheme属性を付けてあげると、そのアクションメソッドでは何も起きなくなります。*4 一つのコントローラー内に沢山アクションメソッドがある場合に便利かもしれませんね。

*1:IPも

*2:これはRequireHttps属性も対応してない

*3:UrlBuilder.Pathにクエリストリング以下を設定するとランタイムエラーになる

*4:でもこういう場合は往々にしてこのメソッドだけhttpアクセスを強制させたかったりするので、Scheme.IgnoreじゃなくてScheme.httpを設定することになると思う

はてなブログのテーマを作っていたらgruntでlessをコンパイルする環境ができていた

  1. はてなブログboilerplateが公開されている
  2. このboilerplateにはコンパイル前のlessとコンパイル後のcssが含まれている
  3. どうせならlessで書いてコンパイルしたい
  4. gruntを使えばlessファイルの保存を検知して自動コンパイルするタスクが組める
  5. gruntを使うにはnode.jsのパッケージ管理ツールnpmが必要
  6. nodebrewならnode.jsのバージョンも管理できる

ということなので、はてなブログのテーマを作ろうとしたら何故かnodebrewのインストールから始めることになった。

nodebrewのインストール

この辺り読んで入れた。僕はnodebrew installしてしまったけど、nodebrew install-binaryにしたほうが速いしいいと思う。
知らずにnodebrew installしたら大変時間が掛かってたいへん大変だった。

gruntのインストール

Web制作で面倒な作業を自動化するビルドツール、Grunt v0.4 入門|Web Design KOJIKA17

このへん。プラグインについては、まずgruntを入れて

npm install grunt --save-dev

lessをコンパイルする grunt-contrib-less

npm install grunt-contrib-less --save-dev

ファイルを監視して何かをする grunt-este-watch

npm install grunt-este-watch --save-dev

サーバーに繋いでLiveReloadする grunt-contrib-connect を入れた。

npm install grunt-contrib-connect --save-dev

grunt-contrib-watchではなくてgrunt-este-watchにしたのは軽いから。

Node.js - grunt-este-watchとgrunt-contrib-connectで軽快ファイル監視とLiveReload - Qiita [キータ]

Gruntfile.js作成

grunt-contrib-lessでlessファイルのコンパイルを自動化する。 - タチコマ好きなエンジニアのブログ

この辺りを参考にさせてもらった。僕のGruntfile.jsはこちら

grunt-contrib-lessの設定は、開発環境用と

development: {
    options: {
        paths: '<%= less.production.options.paths %>'
    },
    files: {
        '<%= projectName %>.css': '<%= projectName %>.less'        
    } 
}

clean-cssで圧縮してcss出力する本番環境用に分けた。

production: {
    options: {
        paths: ['less'],
        cleancss: true
    },
    files: {
        '<%= projectName %>.min.css': '<%= projectName %>.less'        
    }
},

pathsは開発/本番どちらも同じにしたいので、開発は本番のプロパティ値を参照するようにした。cssはプロジェクトのルートに出力するようにした。

ちなみに<%= %>はgruntのテンプレート機能で、自分で定義したものを使ったり、他のプロパティから参照したり、jsをインラインで実行できたりする。詳しくはここ

grunt-este-watchについては、

esteWatch: {
    options: {
        dirs: ['./', 'less/'],
    },
    'less': function(filepath) { return 'less:development' }
}

カレントディレクトリとlessディレクトリのlessファイルに更新があれば、grunt-contrib-lessの開発環境用タスクが動くという感じ。

そしてgrunt-contrib-connectは、

connect: {
    server: {
        options: {
            livereload: true,
            port: 9000,
            open: 'http://localhost:<%= connect.server.options.port %>/view/'
        }
    }
},

こんな感じでlivereload: trueにしておくと、grunt実行時にLiveReloadに必要なスクリプトをhtmlファイルに自動でインジェクトしてくれる。これをやっておけばブラウザのLiveReloadエクステンションとかはいらない。あとopenにURLを指定することでデフォルトのブラウザを使って自動でそのページを開いてくれるようになる。/view/以下にテスト用のindex.htmlなどをおいているのでそこを指定。

CSSコンパイル

あとはプロジェクトのルートで

grunt

を実行すればターミナルが待機状態になり勝手にブラウザでview/index.htmlが開く。そしてlessファイルを修正すると自動でコンパイルしてブラウザ再読み込みが走る。これはたのしい。

貼り付け

良い塩梅にデザインできたら

grunt less:production

で圧縮して出力されたcssはてなブログのデザインCSSに貼り付けておしまい。css出力先をdropboxとかにしてはてなブログ側で外部css読み込みしておけば一々コピペとかしなくて捗ると思う。

はてなブログテーマのらくちん制作環境 - kyabana's blog

でも僕は様々なしがらみがあってやっていない。人間誰しもしがらみを持って生きているからしかたがないことだと思う。

ソースはGitHubに置いておいた。

distkloc/SankakuValidator

Xamarin StudioでNuGetを使う

blogを書くのは久しぶりなので、簡単なことを簡単に書くことにした。
今回は.NETのためのパッケージマネージャNuGetをXamarin Studioで使う方法について書いてみる。

続きを読む