Morning Girl

Web API, Windows, C#, .NET, Dynamics 365/CRM etc..

Blazor で API Explorer を作って色々悩んだお話 #GyutanKaigi2019

先週土曜日に仙台で開催した「牛タン会議2019」でBlazorについての発表をしてきました! 今回はその内容をもうちょっとまとめて、Blogとして公開したものになります。

speakerdeck.com

vsuc.connpass.com

ちなみに、この記事は Blazor アドベントカレンダー 12日目です。めちゃくちゃ遅くなってごめんなさい・・・orz

qiita.com

こんなものを作ってみた

実は私、ちょうど2ヶ月ほど前から Blazor を触り始めたんですが、なんて素晴らしい!  JavaScriptフレームワークが苦手な私でもこれならなんか作って公開できそうだ!(思い違い) と感激していました。

そこで、当時開発でよく悩んでいたスマレジのAPIを手軽に実行できるアプリを作ってみたのです。

f:id:sugimomoto:20191217212509p:plain

スマレジ API はすごく柔軟な機能を提供していて、数多くの操作、リソースをコントロールできるのですが

f:id:sugimomoto:20191217212518p:plain

API リクエストの仕様がちょっと独特かつ、項目が多いので気軽に試すのが難しいかなーというAPIでした。

f:id:sugimomoto:20191217212525p:plain

なので、FacebookMicrosoft が公開しているような API Explorer を作って公開しちゃえば、このつらみから開放されるのではないか!? と考えたのです。

そして作ってみたのがこんな感じのものです。

https://cdatajbuilds.s3-ap-northeast-1.amazonaws.com/sugimototest/smaregi.gif

まだ手直し真っ只中ですが、以下のAzure Web Apps で公開しています。(低料金プランとSignalIR未設定のためか、ちょっと重い・・・)

https://smaregiapiexplorer.azurewebsites.net/

さて、なんとか自分がイメージしていたものが動くところまでは至ったのですが、そこで色々とぶつかっていたので、それを共有したいと思います。

私が遭遇した課題 その1

このAPI Explorer を実装する上で、一番の課題だったところは、一番の機能的ポイントでもある、動的なテーブル形式のInputフォームでした。

f:id:sugimomoto:20191217212534p:plain

スマレジ APIは各カラムのフィルターを柔軟に書けるようになっている反面、この部分のリクエストをJSONで書くのがなかなか大変だったので、この部分の実現が一番の肝でした。

ただ、この動的なInputフォーム動的であるが故に、Blazorの強力なバインディング機能でプロパティに値をバインディングできない(できないよね? できたら教えて下さい)!

バインディングできないと、値のとり方がわかんない!

私の試みた解決策 その1

まあ、しょうがない。それじゃあDOM APIにアクセスして、動的に取ってくればいいんでしょ? と考えたので Blazor で DOM APIにアクセスするライブラリはなーんだ? と探したわけですが

FAQ · aspnet/Blazor Wiki · GitHub

Q: Can I access the DOM from a Blazor app?

You can access the DOM through JavaScript interop from .NET code. However, Blazor is a component based framework that minimizes the need to access the DOM directly.

(あなたはBlazor で DOM にアクセスできるよ! そう、JavaScript ならね!:意訳)

JavaScriptか・・・!

Blazor では JavaScript 相互運用機能(Interop)という、.NET MethodからJavaScriptを呼び出す機能が備わっており、この機能でJavaScriptが取得したDOM APIのデータをJSONなどの形式にまとめて、レスポンスとして .NET 側に返す機能が備わっています。

docs.microsoft.com

できる限り、JavaScriptを書きたくなかった私ですが、ここで諦めてHTMLテーブルのInputをなめて、JSONを返すJavaScriptMethodを書き、対応することにしました。

私が遭遇した課題 その2

しかし、ここでも躓きます。JavaScriptでDOM APIにアクセスして、テーブルの情報を取得することはできました。

ただ、Blazorは各Inputやバインディングの値が変更された際に、DOM要素をレンダリングし直します。

docs.microsoft.com

その結果、JavaScript 相互運用で非同期処理をしていることにより、リクエスト生成ボタンをクリックする(JavaScript が DOM の値を取得する)前に、Blazor が DOMを初期状態に書き換えてしまい、値が取得できないということが発生。

取得できていても非同期処理がレンダリングのタイミングがずれて、裏の変数では値を保持しているのに、レンダリングしている値と一致しないということまで発生しました。

私の試みた解決策 その2

結局私が最終的に試みたアプローチは、テーブルInputが変更される度に JavaScriptで値を取得して、テーブルを構成するためのListプロパティに逐次差し戻し、最新化するというものでした。

以下の「columnInput」がテーブルを構成するための要素をすべて保持しており、

                <BSTable IsSmall="true" IsBordered="true" IsStriped="true" Class="overflow-auto" style="max-height: 200px;">
                    <BSTableHead>
                        <BSTableRow>
                            <BSTableHeadCell>Select</BSTableHeadCell>
                            <BSTableHeadCell>Id</BSTableHeadCell>
                            <BSTableHeadCell>ColumnName</BSTableHeadCell>
                            <BSTableHeadCell>ColumnName(J)</BSTableHeadCell>
                            <BSTableHeadCell>ColumnType</BSTableHeadCell>
                            <BSTableHeadCell>ConditionType</BSTableHeadCell>
                            <BSTableHeadCell>ConditionValue</BSTableHeadCell>
                            <BSTableHeadCell>Description</BSTableHeadCell>
                        </BSTableRow>
                    </BSTableHead>
                    <BSTableBody>
                        @foreach (var column in columnInput)
                            {
                        <BSTableRow>
                            <BSTableCell>
                                <BSBasicInput Class="form-control-sm" InputType="InputType.Checkbox" Value="@column.Select"></BSBasicInput>
                            </BSTableCell>
                            <BSTableCell>@column.No</BSTableCell>
                            <BSTableCell>@column.Name</BSTableCell>
                            <BSTableCell>@column.JapaneseLabel</BSTableCell>
                            <BSTableCell>@column.Type</BSTableCell>
                            <BSTableCell>
                                <BSBasicInput Class="form-control-sm" InputType="InputType.Select" Value="@column.ConditionType" @onselect="@SetColumnInput">
                                    <option></option>
                                    <option>=</option>
                                    <option>like</option>
                                    <option>&lt;</option>
                                    <option>&lt;=</option>
                                    <option>&gt;</option>
                                    <option>&gt;=</option>
                                </BSBasicInput>
                            </BSTableCell>
                            <BSTableCell><BSBasicInput Class="form-control-sm" InputType="InputType.Text" Value="@column.ConditionValue" @onchange="@SetColumnInput"></BSBasicInput></BSTableCell>
                            <BSTableCell>@column.Description</BSTableCell>
                        </BSTableRow>
                            }

                        @if (columnInput.Count == 0)
                            {
                        <tr>
                            <td colspan="8">No Records</td>
                        </tr>
                            }
                    </BSTableBody>

                </BSTable>

SelectボックスなどのInputが操作されるたびに、JavaScriptが値を取り直しています。

        public async void SetColumnInput()
        {
            columnInput = await JSRuntime.InvokeAsync<List<ColumnInput>>("tableDataManager.getTableValues");
        }

これにより、テーブルを構成しているオブジェクトの値を最新に保つようにしています。

Blazor でアプリを作る上で感じたポイント

と、ここまで私の課題と解決アプローチをお話してきたのですが、これが「まっとうな」「ベスト」といえるアプローチに行き着くことができていない、というのが正直なところです。

なので、この発表をした牛タン会議では、最終的に会場に居る素晴らしいMS MVP の方々へ「どうしたらよかっただろう?」というのを聞く! というが本来の目的だったのですが

f:id:sugimomoto:20191217212547p:plain

ちょっと会場の時間が押していたこともあり、なくなく断念となりました(泣

というわけで、せっかくなので、このBlogでもヒドイコードを晒して対応アプローチを募ってみたい、と思っています。

github.com

ちなみに、私がこの経験を通じて得た教訓は「できる限り値はバインディングするに限る。直接的なバインディングなのか、間接的なバインディングに問わず」というものでした。

(こんなBlogでいいのだろうか。)

おしまい

MuleSoft AnyPoint Studio で kintone をデータソースとした Customer アプリ参照APIを作成する:CData kintone MuleSoft Connector

この記事は MuleSoft Advent Calendar 2019 の9日目の記事として投稿しています。

qiita.com

今回この記事では、CData Software で提供しているkintone をデータソースとして、kintone の顧客管理用のアプリを抽象化したAPIを作成してみようと思います。

f:id:sugimomoto:20191209203429p:plain

  • そもそもなぜ抽象化したAPIを作成するのか?
  • kintone Cusomter API の作り方
  • kintone MuleSoft Connector のダウンロード・セットアップ
    • API定義の作成
    • AnyPoint Studio の準備
    • Cusomter APIの処理を実装する
  • おわりに
続きを読む

ASP.NET Core Blazor C# で CData ADO.NET Provider kintone を使う

f:id:sugimomoto:20191129194451p:plain

最近 Blazor を触り始めました!

JavaScriptフレームワークjQueryで止まっている私(Reactはちょこっと触ったけど)にはめちゃくちゃありがたいフレームワークだなと思いながら、楽しんでいます。

Blazorは .NET ライブラリ・既存の .NET エコシステムを流用できるのが大きなポイントです。やっぱり JSON.NET とか REST Clientとか使いたい。

docs.microsoft.com

そして、私の会社で提供している CData ADO.NET Provider も .NET Core 対応をしていたので、これならイケそうじゃん? と思い、試してみました。

続きを読む

Dynamics 365 Business Central のトライアル取得方法と API の使い方

Dynamics 365 Business Central のトライアルは個人的に何回か取得しているんですが、かなり毎回勘所を忘れてしまうので、書き残しておきたいと思います。

また、ざっとAPIを使うための下地も見えたので、OAuthやAzureADへの登録などのあたりも併せて。

続きを読む

ADO.NET ORM の Dapper で CData ADO.NET Provider の使い方

最近 Sansan の中の人のBlogで「今までEntityFrameworksとDapperを使っていたけれど、軽量なDapperを全面的に採用しました」というのを見かけました。

buildersbox.corp-sansan.com

私自身が今まで Dapper を使ったことが無かったので、どんな感じなのかな? という点と、ADO.NET ベースなら、CData ADO.NET Providerも動くでしょ! と思ったので、検証してみた結果をまとめてみたいと思います。

続きを読む

独自スクリプト言語の VS Code Snippet を作ってみた:CData API Script

普段メインのエディタとして、VS Codeを使っています。

簡単なプログラミングから、ログ漁り、PowerShellなどの実行まで、これで一通りやっているんですが、その中の一つに私の会社で出している製品の独自スクリプト言語を書くという仕事(半分趣味)がありました。

こんな感じでXMLを拡張して、製品に独自の拡張機能をもたらす感じのもので、CData API Script と言います。

f:id:sugimomoto:20191029224204p:plain

続きを読む

スマレジ主催のミートアップ smaregi tech talk #1 に参加・LTをしてきましたレポート

今日は、先週大阪で開催されたスマレジ主催の勉強会「smaregi tech talk #1」に参加・LTをしてきたので、そのレポートをお送りします!

smaregiconf.connpass.com

普段仙台で仕事をしている私ですが、スマレジ × Web API ということで、「これは参加しないわけにはいかない!」となり、飛行機で大阪まで飛んで参加してきました。

続きを読む