読者です 読者をやめる 読者になる 読者になる

KatsuYuzuのブログ

.NET の開発をメインとした日記です。

Sapporo ComCamp 2016 #JCCMVP #clrh98 の LT で LightNode Client for AngularJS * TypeScript を話しました。

Community ASP.NET TypeScript AngularJS .NET

2016/02/20 に Sapporo ComCamp 2016 powered by MVPs を開催しました。全国イベントの札幌会場を CLR/H でお手伝い致しました。なので札幌会場はハイパーおやつタイム有りです!
f:id:KatsuYuzu:20160220125234j:plain

Sapporo ComCamp 2016 powered by MVPs

  • The Microsoft DevOps Story
    • Drew Robbins
  • より効果的に業務改善するためのOffice 365活用術
    • 長田 直樹
  • はじめての UWP アプリ開発
    • 宮崎 典行
  • すぐできる気がするクライアントアプリ開発における UX 向上テクニック
    • 菅 祐貴
  • これだけDevOps
    • 長沢 智治

togetter.com
f:id:KatsuYuzu:20160220150009j:plain
会場は株式会社内田洋行さんの U-cala(サッポロファクトリー)です。ありがとうございました。

LightNode Client for AngularJS * TypeScript

LT で紹介しました。

www.slideshare.net

LightNode とは

ASP.NET 上で動作する API です。特徴はとにかく簡単なこと。サーバーは当然のこと、なんとクライアントも。サーバーは { class }/{ method } が URL になります。クライアントは T4テンプレートで自動生成!(.NET, Unity, UniRx 対応)
つまり、サーバーで普通にプログラムかいたら*1、クライアントも出来上がってメソッド呼び出しライクに API を呼べる。簡単!

詳細は下記を参照するといいです。

github.com

Client for AngularJS * TypeScript

その自動生成に AngularJS 用(× TypeScript)を作りましたというお話。*2
こんなサーバーを作ったら、

public class Member : LightNodeContract
{
    public Person Get()
    {
        return new Person
        {
            Age = 29,
            BirthDay = DateTime.Now,
            Gender = Gender.Female,
            FirstName = "foo",
            LastName = "bar"
        };
    }
}

こう呼べます。

this.lightNodeClient.member.get()
    .then((x: LightNode.Person) => console.log(x));

使い方

API は NuGet で、AngularJS 用の T4テンプレート は(現状)GitHubから 1 ファイルを取ってきてください。
LightNode/light-node-client.tt at master · neuecc/LightNode · GitHub
T4テンプレートで API のアセンブリを指定して保存すると TypeScript ファイルができあがります。

<#@ assembly name="$(SolutionDir)\Sample\LightNode.Sample.Server.ForAngularClient\bin\Debug\LightNode.Sample.Server.ForAngularClient.exe" #>

そうしたら AngularJS のモジュールで読み込むだけです。

angular.module("app", ["lightNode"])
    .constant("rootEndPoint", "http://localhost:12345/api")
    .config(
    ["lightNodeClientProvider", "rootEndPoint",
        (lightNodeClientProvider: LightNode.ILightNodeClientProvider, rootEndPoint: string) => {
            lightNodeClientProvider.rootEndPoint = rootEndPoint;
            lightNodeClientProvider.timeout = 5000;
        }]);

注意として、プロジェクト構成は API になるクラスと Web サイトのホストは分けたほうがいいです。T4 のアセンブリ解析でアセンブリを掴んでしまって自動生成をもう一度行おうとしてもエラーになります。
自動生成がエラーになるとクライアントが吐き出されないので TS がコンパイルエラーになって、TS がエラーになるとプロジェクトもコンパイルエラーになります。そうするとアセンブリーは掴まれてる、コンパイルできないからアセンブリを一新できない、で詰みます。
こんな感じで別プロジェクトにしておくと、API 側のプロジェクトをビルドしてアセンブリを一新してやれば、Web サイト側のプロジェクトで自動生成を実行できます。
f:id:KatsuYuzu:20160222024842p:plain

AngularJS クライアント

ざっくり説明で下記を生成しています。

  • AngularJS の module "lightNode"
    • provider "lightNodeClientHandler"
    • provider "lightNodeClient"
  • サーバー対応の型
    • 返却で使われる Class*3
    • 返却、パラメーターで使われる Enum

provider のシグネチャは下記の通り。差し替えたり、config したりできます。

export interface ILightNodeClientHandlerProvider {
    lightNodeClientHandlerFactory: ($http: ng.IHttpService) => LightNodeClientHandler;
}

export interface ILightNodeClientProvider {
    rootEndPoint: string;
    defaultRequestHeaders: { [key: string]: string };
    timeout: number;
    lightNodeClientFactory: ($q: ng.IQService, rootEndPoint: string, $http: ng.IHttpService) => LightNodeClientBase;
}

.NET の HttpClient を意識したのでハンドラーを差し替えることで認証挟んだりできるかと思います。

また、Task ライクにキャンセラーを渡しておけばキャンセルできます。

private cancellationTokenSource: LightNode.CancellationTokenSource;

public getMember() {

    if (this.cancellationTokenSource) {
        this.cancellationTokenSource.cancel();
    }

    this.cancellationTokenSource = this.lightNodeClient.createCancellationTokenSource();

    return this.lightNodeClient.member.get(this.cancellationTokenSource.token);
}

キャンセラーの中身は $q サービスで、$http のコンフィグでタイムアウトに指定しておくと、deferred.resolve()abort されるっていうのをラップしてます。
その他、JavaScript で WebApi を扱ってて面倒だなーと思う下記らへんに対応しています。

  • JSON から Date 型を復元
    • 現状、サーバーが JsonNetContentFormatter の場合のみのサポート*4。JSON では文字列なのでフォーマッター次第。)
  • JSON から Class を復元
  • JSON から Array を復元
  • boolean パラメーターを !! で評価
    • 一度も触ってないチェックボックスの ng-modelundefined

まとめ

めっちゃ便利!

*1:変な意識せずに済むって意味で

*2:汎用ではなく AngularJS ピンポイントなのは要り様だったから。

*3:Class のパラメーターはサーバーが非対応

*4:確認してないというだけ