tekitoumemo’s diary

C#、ASP.NET MVC、その他の言語やらツールのブログを書いています。たまに車や雑談するときがあります。

【ASP.NET MVC.C#】ViewEnginesでランディングページを効率良く量産する方法

ランディングページをサイトに組み込むとき、よくデザイナーさんにこんな質問されます。

「どこのファイルを編集すれば良いですか?」
「どんなurlになりますか?」
「ページ作ったら教えてください」

ちゃんと説明して作ってもらえるようにした方が良いのでしょうが、自分で動いてもらってプルリクエストを受けるだけの流れを作れると効率よくなると思います。なので、今回はViewEnginesを使ってデザイナさんでも効率よくランディングページを運用できるものを紹介します。

ランディングページとは?

1つの商品、サービスを売るための1枚の長いWEBページのことを指しており、広告などの流入元となるページとしてよく使われます。季節的な商品や業種などのターゲットに適したマーケティングができるので非常に重要なものとなります。

どうやって効率を上げていくか?

今回のケースでは、エンジニアとデザイナのやり取りが頻繁に起こるので、そのやり取りを極限まで上げる方法です。具体的には以下の通りです。

宣伝したい商品:セーター

デザイナ「うちの商品は軽くて着心地が良いセーターを売っているので、「lp/lightsweater」というURLでLPを作ろう!」
デザイナ「…(モクモクこなす)」
エンジニア「…(自分の業務をモクモクこなす)」
デザイナ「出来ましたー!」
エンジニア「おぉ!(なにもしてない)」

これが理想的なフローです。

では、コードの解説に移ります。全体的なコードは以下です。

        public ActionResult lp(string name)
        {
            ViewEngineResult result = ViewEngines.Engines.FindView(ControllerContext, name, null);
            if (result.View != null) 
            {
                return View(string.Format("~/Views/lp/{0}.cshtml", id));
            }
            return HttpNotFound();
        }

ViewEnginesクラスのEnginesプロパティのFindViewメソッドを使ってパラメータで指定された名前と一致したViewを取得します。

      ViewEngineResult result = ViewEngines.Engines.FindView(ControllerContext, name, null);

Viewプロパティに検索結果が返ってくるので存在したらViewの場所を指定してreturnすると指定したビューが表示されます。それ以外はHttpNotFoundを指定すると404のページが表示されます(エラー処理してね)

if (result.View != null) 
{
     return View(string.Format("~/Views/lp/{0}.cshtml", id));
 }

セキュリティの懸念とかちゃんと考えてない実装方法ですが、ランディングページなど置く場所をちゃんと運用すればかなり実用的な方法になるかと思いますし、実際に業務ではこちらで運用しています。

【ASP.NET MVC, C#】部分ビューでフォームとAjaxを扱う

f:id:tekitoumemo:20180220214244p:plain
MVCで開発してて便利な部分ビューがありますが、フォームとAjaxを扱うことでめんどくさいエラー処理やモーダルで扱う入力処理などめちゃくちゃ楽になったので書きます。サンプルは以下に貼っておきます、参考程度に。
github.com

「何言ってるかわからない」

と思う人も多いかと思いますので、事例を使ってどういうときに便利なのか説明します。
以下のようなモーダルで表示するログイン画面がありますが、モーダル以外にも独立したログイン画面があって複数書くのがめんどくさかったり、冗長になってしまいます。さらにWEBサービスで導線を考えるときにコンバージョンが会員登録、問い合わせになったりするのでいろんなところから多様して使うことも非常に多いです。
f:id:tekitoumemo:20180220215056p:plain
しかもこのような会員登録処理や問い合わせ画面は、エラー処理などアノテーションで対応するのでフォームを使っていることが多く、がちがちに処理が固定されているので汎用的に使えなく結局は別で作成するという羽目になったりします(実際にこういう仕事をしました>馬鹿だった)そこで、部分ビューとして固定されているフォームを使ってアノテーションで判定されたエラーをAjaxで処理出来るとそのまま扱えて楽ちんだねって手法を見つけたのでどのようにやったか説明します。

まずはGetアクションを用意します。ChildActionOnlyは子ビューでしかつかえないよ!ってやつなので僕は必ずつけます。モデルは必要に応じてアノテーションを付けときます。アノテーションのメッセージはデフォルトだと使い物にならないのでErrorMessageを使ってそれとなく通じるメッセージに変更しておきましょう。今回は指定しません。

        [ChildActionOnly]
        public ActionResult Child()
        {
            var model = new ChildModel();
            return View(model);
        }
...
    // 子アクションに使うモデル
    public class ChildModel
    {
        [Required]
        [StringLength(10)]
        public string Name { get; set; }
        [Required]
        [StringLength(255)]
        [EmailAddress]
        public string Email { get; set; }
    }

ビューを用意します。簡単に名前とメールアドレスのテキストとsubmitボタンを用意します。Layout はヘッダー、フッターのデザインマスタを指定しますが部分ビューなので空にしておきます。

@{
    ViewBag.Title = "Child Page";
    // 子ビューだからいらない!
    Layout = "";
}

<div id="MailForm">
    <div class="col-md-4">
        <h2>Child Action</h2>
        @using (Html.BeginForm("Child", "Home", FormMethod.Post))
        {
            @Html.ValidationSummary("")
            @Html.AntiForgeryToken()
            @Html.TextBoxFor(model => model.Name)
            @Html.TextBoxFor(model => model.Email)
            <input id="send" type="submit" value="送信" />
        }
    </div>
</div>

次にPostメソッドを用意します。フォーム通信には必ずValidateAntiForgeryTokenを付けましょう。ValidateAntiForgeryTokenはフォームで生成したトークン(Html.AntiForgeryToken())をチェックし、違っていたら処理をはじいてくれます、超便利。ModelState.IsValidはモデルの状態を表していて、アノテーションでチェックに引っかかった場合にFalseとなります。なにも処理せずにViewを返すと@Html.ValidationSummary("")でリストタグとしてエラーを勝手に表示してくれます。

        [HttpPost]
        [ValidateAntiForgeryToken()]
        public ActionResult Child(ChildModel model)
        {
            if (ModelState.IsValid)
            {
                // 成功
                var result = new ContentResult() { Content = "Success" };
                return result;
            }
            
            return View(model);
        }

次に通信するためのAjaxを用意します。Ajaxはヘルパーでも使えるのですが、あまり使いやすくない(知識ないだけ)のでゴリゴリに書きました。ここでのコツはevent.preventDefault();で送信処理をキャンセルしてください、2回Postされちゃうので。$('form').serialize();でフォームの値が取得できるのでそれをAjaxにセットします。functionでエラーの場合はHTMLがごっそり返ってくるのでJqueryのDom操作を使ってごっそり入れ替えます。そうすると、エラーの場合はフォームで生成されたエラーエッセージが@Html.ValidationSummary("")を配置した場所にリストタグとして出力され、成功した場合は他の処理をすればよいだけです。

$(document).on('click', '#send', function (event) {
        // HTMLでの送信をキャンセル
        event.preventDefault();
        // formデータを取得&送信
        var formData = $('form').serialize();
        $.ajax({
            async: false,
            url: ' /Home/Child',
            type: 'POST',
            data: formData,
            timeout: 10000,
            dataType: 'text',
            success: function (data) {
                if (data == "Success") {
                    location.href = "/";
                } else {
                    //エラーの場合、MailFormのDomを差し替える
                    $("#MailForm").html(data);
                }

            }
        });
    });

これらを駆使すれば、フォームを使っていた処理をそのまま使えてAjax通信でリロードしなくてもエラーメッセージが使えるので非常に便利です。子ビューは覚えるといっぱい使いたくなりますが、いろんなところで扱うビューだけにしたほうがよいです。僕も結構好きでいろいろ使いましたがわかりにくくなっちゃったりしたので微妙でした、Angularなどのコンポーネント志向にどっぷりつかる感じと似てるかも。

割とメジャーな技術なので、知ってる方も多いと思いますが、この記事でちゃんとしたところ(無駄に子ビューを作らない)で汎用的に部分ビューを扱えればと良いなと思います。

【C#、ASP.NET MVC】独自クラスのプロパティをNULL以外の値で初期化する


技術ネタというよりはテクニックネタ。

独自クラスのプロパティを初期化をするときにStringやらNullableやらリストやらをdefault(T)で初期化するとNULLになってしまいます。

default(T)とは


単純に初期化をするだけなのですが、ジェネリックで指定出来るので0やらNULLやらと型によって値を変えなくて良いやつです。

規定値がNULLだとビミョーなのか

規定値がNULLなのでその通りに扱えば良いのですが、現場では意外に規定値以外の初期化は必要になったりします。例えば、entityframeworkやDapper※はテーブルと同じ形のモデルクラスを持っていて、とあるテーブルのカラムがNULL許容していたらNullable<T>となってしまいます。実際にテーブルのモデルをビューモデルで扱ったりすると、0と表示させたい場合もあるのでNULLは扱いにくかったりします。
※Dapperは独自でテーブルのモデルクラスを作るので、全く同じとは限りません。entityframeworkもモデルクラスをカスタム出来るので、全く同じとは限りませんが、自動で作るという点で考えると全く同じという認識でも問題ないかもしれません。

今回はクラスのプロパティを一括してNULL以外の規定値に変えるプログラムを作ったので、解説します。今回のプログラムはNULLは0やnewなどの初期化をし、すでに初期化されているプロパティは変換しません。

なぜこれを作ったのかというと、それぞれのビジネスロジックやモデル内で以下のような初期化コードを見つけたりします。

model.hoge1 = 0;
model.hoge2 = ””;

べつにこれでもいいっちゃいいのですが、無法地帯になると肥大化しまくります。ここら辺の問題はモデルに紐づくのでモデルのベースクラスに定義します。

     /// <summary>
    /// Baseクラス
    /// </summary>
    public class BaseClass
    {
        /// <summary>
        /// 特定の規定値で初期化を行う
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void InitializingAbstractionClass<T>()
        {
            var properties = typeof(T).GetProperties();
            foreach (var property in properties)
            {
                var value = typeof(T).GetProperty(property.Name).GetValue(this, null);

                // 値がない場合に初期化
                if (value == null)
                {
                    var returnType = property.GetMethod.ReturnType;

                    // string型は既定値を空白にする
                    if (returnType == typeof(string))
                        value = string.Empty;
                    // Nullable型は既定値を0にする
                    else if (returnType == typeof(Nullable<byte>))
                        value = (byte)0;
                        ・・・
                    // その他、自前クラス等を初期化する
                    else
                    {
                        var currentType = Type.GetType(typeof(T).GetProperty(property.Name).PropertyType.FullName);
                        value = Activator.CreateInstance(currentType, null);
                    }
                }
                property.SetValue(this, value);
            }
        }

まず、ジェネリックで渡されたTypeからプロパティ一覧を取得します。

typeof(T).GetProperties();

プロパティのGetMethod.ReturnTypeからプロパティのTypeを取得し、Typeの初期化を行います。

if (returnType == typeof(string))
    value = string.Empty;
// Nullable型は既定値を0にする
else if (returnType == typeof(Nullable<byte>))
    value = (byte)0;

独自クラスなどnewしなければいけないプロパティについては、インスタンスを作成します。

Activator.CreateInstance(currentType, null);

最後にSetValueするだけです。

SetValue(this, value);

このクラスをビューモデルなどに継承させ、初期化の度に呼び出したり、コンストラクタに利用したりしてもよいと思います。以下のような形で呼び出します。

model.InitializingAbstractionClass<Hoge>();

以下のクラスがすべて初期化されました。

/// <summary>
/// Hogeクラス
/// </summary>
public class Hoge : BaseClass
{
    public Nullable<byte> Hoge1 { get; set; }
    public Nullable<short> Hoge2 { get; set; }
    public Nullable<ushort> Hoge3 { get; set; }
    public Nullable<int> Hoge4 { get; set; }
    public Nullable<uint> Hoge5 { get; set; }
    public Nullable<long> Hoge6 { get; set; }
    public Nullable<ulong> Hoge7 { get; set; }
    public Nullable<float> Hoge8 { get; set; }
    public Nullable<double> Hoge9 { get; set; }
    public Nullable<decimal> Hoge10 { get; set; }
    public string Hoge11 { get; set; }
    public ChildHoge Hoge12 { get; set; }
}

// 結果
Hoge1 = 0
Hoge2 = 0
Hoge3 = 0
Hoge4 = 0
Hoge5 = 0
Hoge6 = 0
Hoge7 = 0
Hoge8 = 0
Hoge9 = 0
Hoge10 = 0
Hoge11 = ""
Hoge12 = ChildHoge()

これで無駄な初期化処理がなくなりコードがすっきりするので、ジェネリックおすすめです。正直、ざっと作ったので実用的かといわれると微妙ですが、参考程度にこんなものもあるんだぐらいの認識でよいかと思います。

PS:動的Typeをキャストする方法ってないの?
Convert.ChangeType メソッド (Object, Type) (System)
これだとキャストできなかったりするので、簡単な方法でやり方を知っている人がいたら教えてください。

ブライダルフェアは全然お得ではない


今年に籍を入れたんで、式場を探すためにブライダルフェアに行きまくってます。ブライダルフェアでは一万円ぐらいする食事が振舞われたり、非日常的なことがあるので「面白いよー!休日暇潰れるよー」って良く言われます。なんかお得で楽しそうに思うのですが、実際、
クソだるい!
ので、ブライダルフェアはあくまで式場を見つける手段で遊びに行かない方が良いですよって話です。まず、クソだるい点は三点あります。

時間がクソ長い

長いよまじで。3〜5時間するよ。仕事しててだいたい安く見積もっても時給1500円で7500円の価値あるのか?って聞かれたらまずないです。試食会とかぶっちゃけいらないから一時間にしてほしい。一日中は流石に休日勿体無いですよ。

試食会は微妙

めっちゃうまい!クッソ高級だけど「どうしても今日はフォアグラ食べてたい!フィレ肉食べてたい!」ってならないよ、ラーメンとかはあるけど。しかも良くないところだとワンプレートとかあるので、長く拘束される割に飯とかどうでもいいなぁ。少なくとも先月5回行った僕はもうフォアグラとかしばらくいらんです。ただ、T&Gの飯は美味すぎた。式を挙げなくてもいいから試食会は行った方がいい(笑)

プランナーがだるい

ムービーをめっちゃ見せてくるし、アンケートとか 何枚も書かせてくる。最悪なところなんか、試食会とか言ってチョコボールレベルのおやつしか出さないところもある。終わってるプランナーなんかは「いつまでに決めないと行けませんねー」とか言ってくる。何百万の買い物をすぐ決められるわけないだろ、馬鹿かよ。

ここまででもクソだるいのですが、金額が金額なので、精神衛生上よろしくないです。まず、普通の20代なら結婚式を挙げる貯金を持ってる人は少ないです。持っていたとしても、ほぼ貯金がなくなったり、持ってない人は資金調達を考えなければ行けません。さらに相見積もり取って式場を見てる時間などないので、焦りと不安にやられます。大袈裟かも知れませんが、ブライダルフェアに行ったあとはかなり疲労が溜まるので小さいストレスがかかっています(特に男性は)。かと言って結婚式を挙げなくてもいいのかと言うと、親が悲しんだりするのでそうもいきません。なので、月に2回ぐらいブライダルフェアに行ってちょっと楽しみになるぐらいが丁度良いと思います。あと何個か候補を用意して、行きたくない順で式場を回った方が良いと思います、1回目で式場を決めちゃう人が多いらしいので(ベンツとか高級車を買えるぐらいの値段はするのでちゃんと決めた方がいいと思います!)。

ブライダルフェアは遊びに行くとこと勘違いしてましたが、相手も高額商品として僕らを見てるので本気で説明してくれます。せっかくの晴れ舞台ですので、気疲れして式場を決めないようにしましょう。遊びに行くなら式場決めてから昼飯がてらに行くのも良いかと思います!

【Azure】Custom Vision Serviceの触りを説明する


Custom Vision Service

AIブームに乗っかってAzureのCustom Vision Serviceを触ってみました。UIで使えるので、使い勝手が良く気軽に出来るためAIが身近なものに感じます。今回はすごく簡単にですが、CustomVision特有のものについて説明します。

Custom Vision Serviceとは?

去年にマイクロソフトから発表された新しい技術で、自分でAI作れまっせ!っていうやつです。今までは、マイクロソフトが人口知能を学習させてある程度使えるAIとして発表していましたが、Custom Vision Serviceはすごくマニアックな画像を学習させて独自のAIを作れちゃうよ!ってことで話題になっています。

例えばこんなAI作れちゃうよってのがわかる一例

アホだね。(褒め言葉)

すごく簡単に使えます!

まじで誰でも使えます!エンジニアじゃなくても。

簡単ですね。

APIの使い方

以下のリンク見ればだいたいわかると思いますが、CustomVision特有のパラメーターを説明します。
Custom Vision Serviceのドキュメント

Training-key
headerに設定します。CustomVisionをトレーニングさせるために必要な認証キーと言ったところでしょうか。こちらは[Account Settings]から確認出来ます。

Project Id
URLの一部に設定します。プロジェクト単位のIDです。CustomVisionは複数のプロジェクトが作れるのでこちらも必ず指定します。複数のプロジェクトが作れるので、「ひたすらとろサーモン久保田に似てる人を見つけるAPI」など状況に適したものが作れるのも魅力です。

iterationId
イテレーションと言い、日本語では「繰り返すこと、反復」でCustomVisionでは学習させた単位を指します。添付の画像のように5枚学習させたAI、10枚学習させたAIなど制度が一番高い学習単位でAPIが使えます。

iterationIdがどこにあるかわからなかったのですが、添付した画像のように引っこ抜いてきました。

使った感想

CustomVisionは学習させないと何も出来ません。なのでむやみやたらに「これは結婚式だぞー」って画像を学習させてたらバカな子になります(白い洋服着てれば結婚式っしょって言われます)。3歳ぐらいの子に「これは結婚式の写真だよ、これは白い洋服着てるけど結婚式じゃないよー」って教えてあげるのと同じ感覚です。僕は前者でガンガン学習させてたのですが、逆にどんどん精度が下がってしまい、学習させてるつもりが学習させられた感覚になってしまいました。GoogleHomeにしてもAIの技術はまだまだ人間の仕事を奪うに値しないレベルだと思っているので、残念ながらあと10~20年は働かなければいけなさそうです。残念!

Postmanを使って超簡単手抜きプログラミング

f:id:tekitoumemo:20180125231431p:plain

Web APIをテストする際に非常に便利なツールであるPostmanですが、テストだけでなくリクエストした処理のソースコードを作ってくれるので重宝しています。今回はコードの発行方法を書きたいと思います。

Postmanとは?

www.getpostman.com
Web APIのテストクライアントサービスで、多機能なのに使いやすくて非常に便利です。今回はソースコードの発行方法を中心に書くので、使い方は以下のリンクを参考にしてください。すごく丁寧に説明してくれています。
dev.classmethod.jp

超簡単手抜きプログラミング

postmanのリクエストを設定します。今回はiTunes Search APIのリクエストをサンプルとして使います。
f:id:tekitoumemo:20180125232548p:plain
今回はiTunesから「ジャスティンビーバー」というキーワードで上位10件を取得するリクエストを投げました。

https://itunes.apple.com/search?lang=ja_jp&term=ジャスティンビーバー&limit=10

次に[Send]ボタンを押下するとレスポンスが返ってきます。
f:id:tekitoumemo:20180125232705p:plain
簡単ですね!もちろんPOST、PUTなどのRESTful APIで使うメソッドはすべて使えます。

次に[Code]というリンクをクリックします。
f:id:tekitoumemo:20180125233203p:plain
そうするとさっそくAjaxのコードが表示されました。
f:id:tekitoumemo:20180125233324p:plain
あとは右上のプルダウンメニューで使いたい言語を選択するとそのコードが表示されます。今回はC#を選択しました。
f:id:tekitoumemo:20180125233435p:plain
あとはこのコードを貼っつけておしまい。めっちゃ簡単ですね!

ソースコードを見てもらえればわかると思いますが、ヘッダーに「postman-token」が含まれてたり、いつも使ってないライブラリが使用されていたりしますのでちゃんとした開発をする場合はあくまで参考程度に。Google Analytics APIやSlack APIなど社内で使うツールを作ったり、新人研修なんかは非常に良いですね!

ぜひ使ってみてください。

これ、面白そうなもの作れそうじゃん!って思うAPI5選


常になんか作りたいなーっていろいろ考えているのですが、アイデアが浮かんでも実装するのに時間が掛かったりするので、よくWeb APIを検索しています。楽しそうなAPIがいくつかあるので、ここでご紹介します。

まずWeb APIとはなにか?

Application Programming Interface」と言ってアプリケーションを操作するために必要なやりとりのことです。要はアプリケーションを通常動作と異なるかたちで操作出来るぜ!ってやつですね(厳密には違うけど、だいたいあってるっしょ)。これらを使えば、すごく時間をかけて作ったアプリやデータ群を使えてめっちゃ便利だね!ってことですごく魅力的なものです。

前置きは終わったんで面白そうなAPIを紹介します。

Web Speech API


言語認識系のAPIは多いですが、これはブラウザ上で言語認識をしてくれます。めっちゃいいなーと思ったのは、言語認識系のAPIは従量課金が多くお金が掛かったり、Webサーバにも負荷を掛けるのでそこらへんの問題を全て解決出来ているのはすごく素敵です。まだ、開発中でchromefirefoxしか使えませんがWeb上でモバイルアプリのようなことが出来るかもしれません。いつか僕は絶対使う!

Youtube Data API v3


ベタですんません。今や普通のメディアとしてYoutubeを見ていますが、コンテンツ系のサイトとか作る場合は必ず必要なものになっています。シンプルに再生数やサムネイルなど取れるので結構使えます。実は僕の作っているサイトにも使っています。このサイトはちゃんと紹介したいので今度ブログ書きます。

Microsoft Translator テキスト API


マイクロソフトの翻訳API。bing翻訳と全く同じ機能を使っていて、微妙に使いづらいです。なんでオススメかと言うと、なんと月に200万文字まで無料と言う太っ腹ぶり。Googleはお金かかるっぽいしね〜。翻訳機能は微妙と言う声は多いですが、僕みたいな英語が全くわからん人でも翻訳すればだいたい理解出来ます。今の若い人は自分で情報を取るのが非常に上手なので、海外のコンテンツを翻訳するだけで結構面白いサービスが作れそうですね!

マストドン


WebAPIではないのですが、割と面白いので。クローズドなTwitterを簡単に作れるよってやつで比較的使う場面はありそう。Railsで作られていてオープンソースなのでherokuあたりでサブドメイン作ればコミュニティサイトみたいな感じで運用出来そう。

ガラポンTV


テレビ録画機のメーカーが出してるAPI。全くもって使い道が思いつきませんが、テレビ情報を取得して流すって言う発想が面白いと思います。月額2万もそこそこリーズナブルです。っかマジでどこに使うんだ?これ笑

様々なAPIを紹介しましたが、まだまだいくらでも面白いAPIがあります。あくまで自分の作ったサービスにアクセントを付けるためにあるので、最適なAPIを見つけるのも楽しいかと思います。素晴らしいAPIを使ってより良いサービスを作れたらいいですね!