Fable.Remoting は、Fable および .NET アプリ用の RPC 通信レイヤーであり、Http と Json を抽象化し、コンパイル時に静的にチェックされる純粋なステートレス関数の観点からのみクライアントとサーバーの対話を考えることができます。
このインターフェイスは、各フィールドがAsync<'T>
またはAsync<'T>
を返す関数のいずれかであるレコード タイプです。
type IGreetingApi = { 挨拶 : 文字列 -> 非同期<文字列>}
letgreetingApi = { greet = fun name ->async { letgreeting = sprintf "Hello, %s" name returngreeting}}// 実装を HTTP サービスレットとして公開する webApp = Remoting.createApi() |> Remoting.fromValuegreetingApi
// サービスレットの型付きプロキシを取得しますgreetingApi = Remoting.createApi() |> Remoting.buildProxy<IGreetingApi>// serviceasync の使用を開始する { させて! message =greetingApi.greet "世界" printfn "%s" メッセージ // Hello, World}
それだけです。HTTP や JSON はなく、すべてタイプセーフです。
SAFE-TodoList シンプルなフルスタック Todo リスト アプリケーション (初心者向け)
tabula-rasa 現実世界風のブログ プラットフォーム (中級)
イベントソーシングを使用して実装された Yobo ヨガクラス予約システム (上級)
このライブラリはバックエンドのあらゆる場所で実行されます: Suave WebPart
として、Giraffe/Saturn HttpHandler
として、または Asp.NET Core ミドルウェアとしてのその他のフレームワークとして。クライアントは Fable または .NET アプリケーションです。
「Fable.Remoting は、コンパイル時にフロントエンド コードとバックエンド コードの同期を保つという長年の問題を、F# と同じくらい楽しい言語で解決します。」 - David Falkner
Fable.Remoting がすでにセットアップされており、すぐに使用できる SAFE Simplified テンプレートを使用します。
図書館 | バージョン |
---|---|
Fable.Remoting.MsgPack | |
Fable.Remoting.Client | |
Fable.Remoting.Json | |
Fable.Remoting.Server | |
寓話.リモート.スワーブ | |
寓話.リモート.キリン | |
Fable.Remoting.AspNetCore | |
Fable.Remoting.DotnetClient | |
Fable.Remoting.AzureFunctions.Worker | |
Fable.Remoting.AwsLambda |
新しい F# コンソール アプリを作成します。
dotnet new console -lang F#
クライアントとサーバー間で共有するタイプを定義します。
// SharedTypes.fsmodule SharedTypestype Student = {Name : stringAge : int}// サーバーとクライアント間の共有仕様 IStudentApi = {studentByName : string -> Async<Student option>allStudents : Unit -> Async<list<Student>>}
IStudentApi
タイプは非常に重要です。これはサーバーとクライアント間のプロトコルの仕様です。 Fable.Remoting
そのような型が最終結果でAsync
返す関数のみを持つことを期待します。
非同期<A>A -> 非同期<B>A -> B -> 非同期<C>// など...
後でクライアントからこれらのファイルを参照できるように、そのようなタイプを別のファイルに配置してみてください。
次に、サーバー上にIStudentApi
の実装を提供します。
SharedTypeslet を開く getStudents() = async {return [{ Name = "マイク"; 年齢 = 23; }{ 名前 = "ジョン"; 年齢 = 22; }{ 名前 = "ダイアナ";年齢 = 22; }] } let findStudentByName name = 非同期{させて!学生 = getStudents()let 学生 = List.tryFind (楽しい学生 -> 学生.名前 = 名前) 学生を返す学生 }letstudentApi : IStudentApi = {studentByName = findStudentByName allStudents = getStudents}
studentApi
の実装ができたので、これをさまざまな Web フレームワークから Web サービスとして公開できます。スワーブから始めます
Packet を使用して Nuget からライブラリをインストールします。
paket add Fable.Remoting.Suave --project /path/to/Project.fsproj
値studentApi
からWebPartを作成する
open Suaveopen Fable.Remoting.Serveropen Fable.Remoting.Suavelet webApp : WebPart =Remoting.createApi()|> Remoting.fromValue StudentApi|> Remoting.buildWebPart// Web サーバーを開始startWebServer defaultConfig webApp
はい、それはとても簡単です。 webApp
値は、疑似コードで次のように考えることができます。
webApp = にしてみましょう 選ぶ [ POST >=> パス「/IStudentApi/studentByName」 >=> (* リクエスト本文 (json から) を逆シリアル化 *) >=> (* 逆シリアル化された入力を使用して StudentApi.getStudentByName を呼び出します *) >=> (* クライアントに出力をシリアル化して (JSON に) 返します *) // 他のルート ]
Fable.Remoting.Server から診断ログを有効にして (推奨)、ライブラリが舞台裏でどのように魔法を実行しているかを確認できます:)
let webApp =Remoting.createApi()|> Remoting.fromValue StudentApi|> Remoting.withDiagnosticsLogger (printfn "%s")|> Remoting.buildWebPart
packet を使用して Nuget からパッケージをインストールする
paket add Fable.Remoting.AspNetCore --project /path/to/Project.fsproj
これで、リモート ハンドラーを AspNetCore ミドルウェアとして構成できるようになりました
let webApp =Remoting.createApi()|> Remoting.fromValuestudentApilet configureApp (app : IApplicationBuilder) =// Remoting ハンドラーを ASP.NET Core パイプラインに追加しますapp.UseRemoting webApp[<EntryPoint>]let main _ =WebHostBuilder().UseKestrel ().Configure(Action<IApplicationBuilder> configureApp).Build().Run()0
Suave の部分をライブラリのインストールまでたどると、次のようになります。
paket add Fable.Remoting.Giraffe --project /path/to/Project.fsproj
ここで、WebPart の代わりに、 Fable.Remoting.Giraffe
名前空間を開くと、値server
から HttpHandler を取得します。
open Giraffeopen Fable.Remoting.Serveropen Fable.Remoting.Giraffelet webApp : HttpHandler =Remoting.createApi()|> Remoting.fromValue StudentApi|> Remoting.buildHttpHandlerlet configureApp (app : IApplicationBuilder) =// Giraffe を ASP.NET Core パイプラインアプリに追加します.UseGiraffe webApplet configureServices (サービス: IServiceCollection) =// Giraffe の依存関係を追加しますservices.AddGiraffe() |>ignore[<EntryPoint>]let main _ =WebHostBuilder().UseKestrel().Configure(Action<IApplicationBuilder> configureApp).ConfigureServices(configureServices).Build() .Run()0
Giraffe ライブラリによって生成された同じwebApp
使用できます。
Saturnopen Fable.Remoting.Serveropen Fable.Remoting.Giraffelet webApp : HttpHandler =Remoting.createApi()|> Remoting.fromValue StudentApi|> Remoting.buildHttpHandlerlet app = application {url "http://127.0.0.1:8083/"use_router webApp}アプリを実行する
サーバーレス リモーティング サーバーとしてカスタム HttpTrigger を使用して分離モードで Azure Functions を使用するには、以下をインストールするだけです。
dotnet add package Fable.Remoting.AzureFunctions.Worker
またはパケットを使用する
paket add Fable.Remoting.AzureFunctions.Worker --project /path/to/Project.fsproj
Azure Functions は HttpHandler について何も知らないため、組み込みのHttpRequestData
とHttpResponseData
オブジェクトを使用する必要があります。幸いなことに、 Remoting.buildRequestHandler
とHttpResponseData.fromRequestHandler
関数が役に立ちます。
Fable.Remoting.Serveropen Fable.Remoting.AzureFunctions.Workeropen Microsoft.Azure.Functions.Workeropen Microsoft.Azure.Functions.Worker.Httpopen Microsoft.Extensions.Loggingtype Functions(log:ILogger<Functions>) =[<Function("Index ")>]member _.Index ([<HttpTrigger(AuthorizationLevel.Anonymous, Route = "{*any}")>] req: HttpRequestData, ctx: FunctionContext) =Remoting.createApi()|> Remoting.withRouteBuilder FunctionsRouteBuilder.apiPrefix|> Remoting.fromValue myImplementation|> Remoting.buildRequestHandler|> HttpResponseData.fromRequestHandler req
もちろん、Function App ごとに 1 つの実装を持つのは理想的ではないため、 HttpResponseData.fromRequestHandlers
が役に立ちます。
type Functions(log:ILogger<Functions>) =[<Function("Index")>]member _.Index ([<HttpTrigger(AuthorizationLevel.Anonymous, Route = "{*any}")>] 要求: HttpRequestData, ctx : FunctionContext) =let handlerOne =Remoting.createApi()|> Remoting.withRouteBuilder FunctionsRouteBuilder.apiPrefix|> Remoting.fromValue myImplementationOne|> Remoting.buildRequestHandler let handlerTwo =Remoting.createApi()|> Remoting.withRouteBuilder FunctionsRouteBuilder.apiPrefix|> Remoting.fromValue myImplementationTwo|> Remoting.buildRequestHandler [ handlerOne; handlerTwo ] |> HttpResponseData.fromRequestHandlers 要求
Packet を使用して nuget からFable.Remoting.Client
をインストールします。
paket add Fable.Remoting.Client --project /path/to/Project.fsproj
クライアント プロジェクトへの共有タイプを参照する
<Compile Include="path/to/SharedTypes.fs" />
ライブラリの使用を開始します。
open Fable.Remoting.Clientopen SharedTypes//studentApi : IStudentApiletstudentApi =Remoting.createApi()|> Remoting.buildProxy<IStudentApi>async { // 学生 : 学生[] させて!学生 =studentApi.allStudents() for Student in students do// Student : Studentprintfn "生徒 %s は %d 歳です" Student.Name Student.Age}|> Async.StartImmediate
最後に、 webpack-dev-server
使用している場合は、次の構成を変更する必要があります。
開発サーバー: { contentBase:resolve('./public')、 ポート: 8080}
これに:
開発サーバー: { contentBase:resolve('./public')、 ポート: 8080、 proxy: {'/*': { // webpack-dev-server にクライアントからのすべてのリクエストをサーバー target: "http://localhost:5000" に再ルーティングするように指示します。// バックエンド サーバーがポートでホストされていると仮定します。開発中は 5000 ChangeOrigin: true}}
それでおしまい!
コンソール、デスクトップ、モバイル アプリケーションなど、非現実的なプロジェクトでもクライアント機能を使用することもできます。
Packet を使用して nuget からFable.Remoting.DotnetClient
をインストールします。
paket add Fable.Remoting.DotnetClient --project /path/to/Project.fsproj
クライアントプロジェクトで共有タイプを参照する
<Compile Include="path/to/SharedTypes.fs" />
ライブラリの使用を開始します。
open Fable.Remoting.DotnetClientopen SharedTypes//studentApi : IStudentApiletstudentApi = Remoting.createApi "http://localhost:8085" |> Remoting.buildProxy<IStudentApi>async { // 学生 : 学生[] させて!学生 =studentApi.allStudents() for Student in students do// Student : Studentprintfn "生徒 %s は %d 歳です" Student.Name Student.Age}|> Async.StartImmediate
ここで、ドットネット クライアントはバックエンドとは別にデプロイされるため、Fable クライアントとは異なり、バックエンドのベース URL を指定する必要があることに注意してください。
IStudentApi
に別のレコード フィールド関数を追加します
その機能を実装する
サーバーとクライアントを再起動します
終わり!クライアントからもその機能を使用できるようになりました。
このライブラリの実装方法に興味がある場合は、次の記事を参照してください (少し古いですが、メカニズムの概要が説明されています) F# を使用した静的型付きクライアント/サーバー通信: 概念実証