knockout.jsでjsonをQuery Stringに変換する際にハマった。

knockout.jsを使ってる前提でお話します。
例えば、こんなモデルがあるとして

function BlockSearchConditionModel(){
    this.width = ko.observable("");
    this.height = ko.observable("");
}

これをjQueryajaxでGetのパラメータに含めたい場合、

$.ajax({
    type: "Get",
    url: "/hoge/api/v1/blocks/search.json?widht=" + model.width() + "&height=" + model.height(),
    success: function(data){

        // 成功時

    } 
});

とする方法が考えられるが、
この方法だと条件が増えるたびにパラメータを書き換える必要がある。
なので、data: に$.paramを使いたい。

api.jquery.com

こっから本題です。
jsonにして渡すと

$.ajax({
    type: "Get",
    url: "/hoge/api/v1/blocks/search.json",
        data: $.param(ko.toJSON(model)),
    success: function(data){

        // 成功時

    } 
});

0=%7B&1="&2=w&3=i&4=d&5=t&6=h&7="&8=・・・(略)
と、予想外のパラメータが生成されます。

なぜなら、ko.toJSONで生成されるjsonは "{ "width": "10", "height": "20"}"
のように、外っ側に"が付いているのです。。
OH !!!

わざわざjsonにしないで、object渡しましょう。

$.ajax({
    type: "Get",
    url: "/hoge/api/v1/blocks/search.json",
        data: $.param(model),
    success: function(data){

        // 成功時

    } 
});

というお話しです。
1時間近く考え込んでしまった・・。

vmware fusionを使用してmac上のWindowsでVirtual Boxに64bit OSを指定できない場合の対処法

タイトルがなげー。

mac上のvmware fusionで動作しているWindows 7
Virtual Boxをインストールしている場合、
新規マシンの作成時に64bit OSが選択できない問題があります。

ぐぐると、BIOSの設定で
Intel(R) VirtualizationTechnology
を有効にしろとか出てくるんですよ。

fusionから起動しているWindowsにそんなもん無いんですよ。

その場合、fusionから対象のマシンの設定を開いて プロセッサとメモリ f:id:alocoholic_babay:20151112152627p:plain

からの

この仮想マシンでハイパーバイザーアプリケーションを有効化するにチェック。 f:id:alocoholic_babay:20151112152643p:plain

これで解決!

PowerShellでpecoでcdする

Windows使いの皆さん。こんにちは。
PowerShell使ってますか?
僕は普段はmac使いなので、zshでpecoってるわけです。

Pecoって便利ですよね。

PowerShellでも使いたい。
インストールだけなら、Windows版のHomebrewことChocolateyで可能。

choco install peco  

pecoの選択結果でcdしたい

PowerShellスクリプトも、dosコマンドもよく解らないので
適当に作ってみる。

まず、ユーザのホームディレクトリにバッチを設置する。
C:\Users{UserName}\cdp.bat

@echo off

for /f "delims=" %%i in ('dir /ad/s/b^| peco') do (
    echo cd '%%i'
    break
)

次に、Microsoft.PowerShell_profile.ps1を弄ります。
パスがわからない人は、PowerShell

$PROFILE

とすれば、出てきます。
Microsoft.PowerShell_profile.ps1が見当たらない人は、新規に追加して下さい。
そして、こんなfunctionを追加します。

function cdp(){
        $cd = $(~\cdp.bat)
        if($cd){
                Invoke-Expression "$cd"
        }
}

PowerShellを再起動後、

cdp

コマンドを実行すると・・・ pecoが発動!
日本語パスに問題があるが、ちょっと捗る。

c# WebAPIにajaxでclassをPOSTする(RoutePrefix付き)

c# WebAPIにajaxでclassをPOSTする(RoutePrefix付き)

いつも忘れてしまって何故404?と悩むのでメモ。

まず、WebAPIの引数に渡したいクラスを定義する

適当に。EMail、Nameというプロパティを持ったクラスを用意する。
とりあえず、/Models/Users.csを追加。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace WebAPIPostSample.Models
{
    public class User
    {
        public string EMail { get; set; }
        public string Name { get; set; }
    }
}

WebAPIの中身

/Controllers/Api/UsersController.csを追加。
Userを受け取って、何か処理をするという想定。
RoutePrefixを使って、URLにバージョンを埋め込む。
HttpPostを付加しているので、Routeは""になる。
これで
/api/v1/users
にPOSTすると、Postが呼び出される。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using WebAPIPostSample.Models;

namespace WebAPIPostSample.Controllers.Api
{
    [RoutePrefix("api/v1/users")]
    public class UsersController : ApiController
    {
        [HttpPost]
        [Route("")]
        public void Post([FromBody]User value)
        {
            var inputEmail = value.EMail;
            var inputName = value.Name;

            /* Do Anything */
        }
    }
}

ajaxを使用するview

/Views/Home/Index.cshtml
を編集する。

@{
    ViewBag.Title = "Home Page";
}

<div class="row">
    <div class="col-md-12">
        <h2>Ajax Postサンプル</h2>
        <div class="form-group">
            <label for="email">Email address:</label>
            <input type="email" class="form-control" id="email">
        </div>
        <div class="form-group">
            <label for="name">Name:</label>
            <input type="text" class="form-control" id="name">
        </div>
        <div class="form-group">
            <button id="btnDoPost" class="btn btn-primary">送信</button>
        </div>
    </div>
</div>
@section scripts {

<script type="text/javascript">
    $(document).ready(function () {
        $("#btnDoPost").on({
            click: function () {
                var user = { 'EMail': $("#email").val(), 'Name': $("#name").val() };
                $.ajax({
                    type: "post",
                    contentType: "application/json; charset=utf-8",
                    url: "/api/v1/users",
                    data: JSON.stringify(user),
                    cache: false,
                    async: true,
                });

                return false;
            },
        });
    });
</script>

}

ポイント

JSON.stringifyがポイント。
例えば、
data = { EMail: "hoge@fuga.com", "Name": "Hatena太郎" }
なんてしちゃうと、POSTした時に404になってしまうので注意!!

c#でfacebookのAPIを利用してfacebookページに投稿した際に、sandboxモードでハマった話。

ハマったね

タイトルの通り。
APIを使って、facebookページに投稿するのにまずハマりました。
管理者トークンが必要なのね。
それでも、いろんなサイトを見まくって投稿できるところまで行きました。

投稿できたー!
と喜んで、他の人に確認して貰ったところ
APIで投稿したのを確認したハズなのに、他の人のアカウントだと表示されない。

ログアウトしてる場合も、表示されない。

自分しか見えない投稿がPOSTされているのだ。

何故?

サンドボックスモードですって。
My Appの各アプリの名前の横にある緑色の◯ね。
これが、中抜きの◯の場合はサンドボックスモードになっていて
この状態で投稿したPOSTは、世の中に公開されない。
このモードを解除するには、
My Apps → アプリ → Settings で
Contact Emailにメールを登録して、
Status & Review で
Do you want to make this app and all its live features available to the general public?
チェックボックスをONにする必要がある。
気が付かなかった・・・・・。

PowerShellでGitショートカット

GitといえばTerminal

素敵な記事を見かけた。寿命を延ばす事ができるらしい。 postd.cc

しかし、僕はVisual Studio愛好家であり
開発環境はWindowsなのである。(実機はmacだけどね!)

WindowsといえばPowerShell

PowerShellでもエイリアス登録してショートカットすればいいいじゃない!
と思ってた時期がありました。
でも、駄目でした。コマンドラインが渡せませんでした。

Functionにしよう!

というわけで、僕が使っている設定ファイルの一部がこちら。 やたらと使うコマンド達。

remove-item alias:gcm -force
remove-item alias:gl -force
remove-item alias:gp -force
remove-item alias:gps -force

function gs(){ git status }
function ga(){ git add $args }
function gaa(){ git add -A }
function gcm(){ git commit -m $args }
function gcam(){ git commit --amend -m $args }
function gl() { git log }
function glg() { git log --graph --oneline --decorate --all }
function gld() { git log --pretty=format:"%h %ad %s" --date=short --all }
function gls() { git log --stat --decorate }
function glns() { git log --stat --name-status --decorate }
function gls() { git log --stat --decorate --grep=$args }
function gdt() { git difftool $args }
function gp() { git pull $args }
function gps() { git push $args }
function grsh { git reset --soft HEAD^ }
function grhh { git reset --hard HEAD^ }

こいつらを、$profileで出力される設定ファイルに書き込んであげてください。
remove-item aliasをしているのは、組み込みエイリアスと被っちゃうからです。
書き込んだら、.$profileで読み込み直してくださいね。

使用例はこんな感じ

gs
gaa
gcm "コメント"
gps origin master

SignalRとTypeScriptとknockout.jsで簡単なチャットを作成

最近の.netは素晴らしい!!

ずっと封鎖的だった.net環境。
最近は、超オープンですね。C#を愛する人間としては、嬉しい限りです。

SignalRで簡単なチャット

とりあえず、クライアントが文章を入力して送信したら
リアルタイムで配信されるだけの簡単なチャットの作成方法をご紹介します。

環境

技術的な話

SignalRの話を追い求めてこのブログに辿り着いたあなたも、
きっと.net大好きなハズなので、 以下の技術を使用します。

  • SignalR
  • TypeScript
  • Web API
  • knockout.js (個人的に大好き)

スタート

では、早速
新しいプロジェクト > Web > ASP.NET Web アプリケーション
を作成しましょう。

名前は適当に『MyChat』にしておきます。

テンプレートは、Emptyを選んで、Web APIにチェックをいれておきましょう。

そしたら、NuGetで以下を取得します。

  • knockoutjs
  • knockout.TypeScript.DefinitelyTyped
  • jquery.TypeScript.DefinitelyTyped
  • signalr.TypeScript.DefinitelyTyped
  • Bootstrap

取得できたら、SignalR ハブクラス(v2)を追加します。
名前は、ChatHub.csにしておきましょう。
デフォルトでHelloというメソッドがありますが、消しちゃいましょう。
そして、using Microsoft.AspNet.SignalR.Hubs;を追加して、
HubNameという属性を付加します。
この属性は、jsサイドでHubの扱いが楽になるので必須です。

using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;

namespace MyChat
{
    [HubName("ChatHub")]
    public class ChatHub : Hub
    {
    }
}

いい感じです。
混乱するかもしれませんが、以降はこのHubに編集は行いません。

次に、Hubのラッパーを作成します。
なんでもいいんだけど、ChatHubHandlerllerというクラスを追加します。
こいつが、Hubを使ってチャットに新規メッセージが追加された事を
クライアントに送信します。

using Microsoft.AspNet.SignalR;

namespace MyChat
{
    public class ChatHubHandlerller
    {
        /// <summary>
        /// クライアントに新規投稿メッセージを通知します。
        /// </summary>
        /// <param name="msg">新規投稿メッセージ</param>
        public void NotifyChatAdd(string msg)
        {
            var context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
            context.Clients.All.SubscribeChatAdd(msg.Replace("\n", "<br />")); // とりあえず、改行だけ置換
        }
    }
}

SubscribeChatAdd?
と思われるかもしれませんが、こいつは動的な式なので
実行時に解決されます。

実は、SignalRはこれでは動きません。
Owinを使用して立ち上げる必要があります。
StartUp.csというクラスを追加して下さい。

using Microsoft.Owin;
using Owin;

[assembly: OwinStartupAttribute(typeof(MyChat.StartUp))]
namespace MyChat
{
    public partial class StartUp
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

partialなのに、注意!!

次に、Viewから呼び出されるAPIを作成しましょう。
ChatV1Controllerという名前で、Web API コントローラー クラス(v2.1)
を追加します。
今回は、メッセージの新規投稿できれば良いので、Get等は消しておきます。

一応、属性マッピングを使ってAPIのバージョニングを可能にしておきます。
属性ルーティングしたりして、以下の形にして下さい。

using System.Web.Http;

namespace MyChat
{
    public class ChatV1Controller : ApiController
    {

        [Route("api/v1/chat")]
        public void Post([FromBody]string value)
        {
            var chatHub = new ChatHubHandlerller();
            chatHub.NotifyChatAdd(value);
        }

    }
}

/api/v1/chat
でアクセスできるようになります。
こうしておくことで、APIのバージョンを共存させられます。

Web API: The Good Parts、凄く良い本でした。

Web API: The Good Parts

Web API: The Good Parts

お次に、knockout.jsを使用してViewModelを作ります。 Scriptの中に、viewmodelsというフォルダを作成して、
ChatViewModel.tsというTypeScriptを作成しましょう。

/// <reference path="../typings/knockout/knockout.d.ts" />
/// <reference path="../typings/jquery/jquery.d.ts" />
/// <reference path="../typings/signalr/signalr.d.ts" />

/**
 * Chat機能を提供するViewModelです
 */
class ChatViewModel {

    /**
    * ViewModelのバインドを開始します。
    */
    public static StartBinding(elementName: string): void {
        var _vm = new ChatViewModel();
        ko.applyBindings(_vm, $(elementName)[0]);
    }

    /**
     * メッセージ一覧を格納します。
     */
    public ChatMessages: KnockoutObservableArray<string> = ko.observableArray([]);
    /**
     * ユーザのインプットメッセージです。
     */
    public InputMessage: KnockoutObservable<string> = ko.observable("");
    /**
     * SingalRの接続状況のメッセージです。
     */
    public SingalRConnectStateMsg: KnockoutObservable<string> = ko.observable("SingalR未接続");

    // SignalR Hub
    public _Hub: HubProxy = null;

    constructor() {
        this._Hub = $.connection.hub.createHubProxy('ChatHub');

        // チャットメッセージ配信を購読
        this._Hub.on('SubscribeChatAdd', (msg) => {
            this.ChatMessages.push(msg);
            this.InputMessage("");
        });

        // 接続待機
        $.connection.hub.start().done(() => {
            this.SingalRConnectStateMsg("SingalR接続済み");
        });
    }

    /**
     * メッセージの投稿を実行します。
     */
    public PostMessage(): void {
        var self = this;
        $.ajax({
            type: "POST",
            url: "/api/v1/chat",
            contentType: "application/json",
            data: JSON.stringify(self.InputMessage()),
            success: function () { }
        });
    }
}

続いて、View。
Viewに関しては、Razorみたいに動的でも良いですし、
静的なhtmlでも良いです。
今回はhtmlで。 index.htmlを追加しましょう。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <link href="Content/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
    <div id="chat-wrap" style="width: 500px; margin: 0 auto;">
        <h2>My Chat</h2>

        <p id="connection-satus" data-bind="text: SingalRConnectStateMsg"></p>

        <div id="chat_messages" data-bind="template: { name: 'chatMsgTmpl', foreach: ChatMessages }"></div>
        <script id="chatMsgTmpl" type="text/html">
            <div class="panel panel-default">
                <div class="panel-body" data-bind="html: $data">
                </div>
            </div>
        </script>

        <div class="text-center">
            <div style="margin-bottom: 11px;">
                <textarea id="txtChatInput" class="form-control" data-bind="value: InputMessage" rows="5"></textarea>
            </div>
            <button id="btnPostChat" type="button" class="btn btn-primary" data-bind="click: PostMessage">送信</button>
        </div>
    </div>
    <script src="Scripts/jquery-1.10.2.min.js"></script>
    <script src="Scripts/knockout-3.2.0.js"></script>
    <script src="Scripts/bootstrap.min.js"></script>
    <script src="Scripts/jquery.signalR-2.1.2.min.js"></script>
    <script src="/signalr/hubs"></script>
    <script src="Scripts/viewmodels/ChatViewModel.js"></script>
    <script>
        $(function () {
            var _vm = new ChatViewModel();
            ko.applyBindings(_vm, $("#chat-wrap")[0]);
        });
    </script>
</body>
</html>

scriptのバージョンは、各自のバージョンに合わせてくださいね。 それでは、実行してみましょう!

他のブラウザから同じURLにアクセスしてみて、投稿ボタンを押してみましょう。
リアルタイムで内容が反映されます。
すげぇぇぇぇぇえええ!