Fable.Remoting عبارة عن طبقة اتصال RPC لتطبيقات Fable و.NET، وهي تلخص Http وJson وتتيح لك التفكير في تفاعلات خادم العميل الخاص بك فقط من حيث الوظائف عديمة الحالة التي يتم فحصها بشكل ثابت في وقت الترجمة:
هذه الواجهة هي نوع سجل حيث يكون كل حقل إما Async<'T>
أو دالة تُرجع Async<'T>
اكتب IGreetingApi = { تحية: سلسلة -> غير متزامن<string>}
دع تحية API = { Greeting = fun name ->async { Let Greeting = sprintf "Hello, %s" name return Greeting}}// كشف التنفيذ باعتباره خدمة HTTP webApp = Remoting.createApi() |> Remoting.fromValue GreetingApi
// احصل على وكيل مكتوب لـ Servicelet GreetingApi = Remoting.createApi() |> Remoting.buildProxy<IGreetingApi>// ابدأ باستخدام Serviceasync { يترك! الرسالة = GreetingApi.greet "العالم" رسالة printfn "%s" // مرحبًا بالعالم}
هذا كل شيء، لا يوجد HTTP ولا JSON وكلها آمنة من النوع.
SAFE-TodoList تطبيق قائمة Todo بسيط ومتكامل (للمبتدئين)
tabula-rasa منصة تدوين واقعية (متوسطة)
تم تنفيذ نظام حجز Yobo Yoga Class باستخدام مصادر الأحداث (متقدم)
تعمل المكتبة في كل مكان على الواجهة الخلفية: مثل Suave WebPart
، أو مثل Giraffe/Saturn HttpHandler
أو أي إطار عمل آخر مثل برنامج Asp.NET Core الوسيط. يمكن للعملاء أن يكونوا تطبيق Fable أو .NET.
"يحل Fable.Remoting المشكلة القديمة المتمثلة في الحفاظ على مزامنة كود الواجهة الأمامية مع كود الواجهة الخلفية في وقت الترجمة، وبلغة ممتعة للاستخدام مثل F#" - David Falkner
استخدم القالب المبسط SAFE حيث تم إعداد Fable.Remoting بالفعل وجاهز للاستخدام
مكتبة | إصدار |
---|---|
Fable.Remoting.MsgPack | |
Fable.Remoting.Client | |
Fable.Remoting.Json | |
Fable.Remoting.Server | |
Fable.Remoting.Suave | |
Fable.Remoting.Giraffe | |
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}// المواصفات المشتركة بين الخادم وClienttype IStudentApi = {studentByName : string -> Async<Student option>allStudents : وحدة -> Async<list<Student>>}
يعد نوع IStudentApi
مهمًا جدًا، فهذه هي مواصفات البروتوكول بين الخادم والعميل. يتوقع Fable.Remoting
أن يحتوي هذا النوع فقط على وظائف تُرجع Async
في النتيجة النهائية:
غير متزامن<A>A -> غير متزامن<B>A -> B -> غير متزامن<C>// إلخ...
حاول وضع هذه الأنواع في ملفات منفصلة للإشارة إلى هذه الملفات لاحقًا من العميل
ثم قم بتوفير تطبيق لـ IStudentApi
على الخادم:
افتح SharedTypeslet getStudents() = غير متزامن {return [{ الاسم = "مايك"؛ العمر = 23؛ }{ الاسم = "جون"; العمر = 22؛ }{ الاسم = "ديانا"; العمر = 22؛ }] }دع اسم findStudentByName = غير متزامن {دع! الطلاب = getStudents()let Student = List.tryFind (الطالب الممتع -> Student.Name = الاسم) Studentreturn Student }let StudentApi: IStudentApi = {studentByName = findStudentByName جميع الطلاب = getStudents}
الآن بعد أن أصبح لدينا تطبيق studentApi
، يمكنك عرضه كخدمة ويب من أطر عمل ويب مختلفة. نبدأ مع Suave
تثبيت المكتبة من Nuget باستخدام Paket:
paket add Fable.Remoting.Suave --project /path/to/Project.fsproj
قم بإنشاء جزء ويب من القيمة studentApi
افتح Suaveopen Fable.Remoting.Serveropen Fable.Remoting.Suavelet webApp: WebPart =Remoting.createApi()|> Remoting.fromValue StudentApi|> Remoting.buildWebPart// بدء تشغيل خادم الويبstartWebServer defaultConfig 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
قم بتثبيت الحزمة من Nuget باستخدام paket
paket add Fable.Remoting.AspNetCore --project /path/to/Project.fsproj
يمكنك الآن تكوين معالجك البعيد باعتباره برنامج AspNetCore الوسيط
دع webApp =Remoting.createApi()|> Remoting.fromValue StudentApiletconfigApp (التطبيق: IApplicationBuilder) =// أضف معالج التحكم عن بعد إلى ASP.NET Core Pipeappapp.UseRemoting webApp[<EntryPoint>]let main _ =WebHostBuilder().UseKestrel ().تكوين(الإجراء<IApplicationBuilder> تكوينApp).Build().Run()0
يمكنك متابعة الجزء Suave حتى تثبيت المكتبة، حيث سيصبح:
paket add Fable.Remoting.Giraffe --project /path/to/Project.fsproj
الآن بدلاً من WebPart، من خلال فتح مساحة الاسم Fable.Remoting.Giraffe
، ستحصل على HttpHandler من server
القيمة:
افتح Giraffeopen Fable.Remoting.Serveropen Fable.Remoting.Giraffelet webApp: HttpHandler =Remoting.createApi()|> Remoting.fromValue StudentApi|> Remoting.buildHttpHandlerlet تكوينApp (التطبيق: IApplicationBuilder) =// أضف زرافة إلى تطبيق خط أنابيب ASP.NET Core .UseGiraffe webApplet تكوين الخدمات (الخدمات: IServiceCollection) =// إضافة تبعيات الزرافةservices.AddGiraffe() |> تجاهل[<EntryPoint>]let main _ =WebHostBuilder().UseKestrel().Configure(Action<IApplicationBuilder>configureApp).ConfigureServices(configureServices).Build ().تشغيل()0
يمكنك استخدام نفس webApp
الذي أنشأته مكتبة Giraffe.
افتح Saturnopen Fable.Remoting.Serveropen Fable.Remoting.Giraffelet webApp: HttpHandler =Remoting.createApi()|> Remoting.fromValue StudentApi|> تطبيق Remoting.buildHttpHandlerlet = التطبيق {url "http://127.0.0.1:8083/"use_router webApp}تشغيل التطبيق
لاستخدام وظائف Azure في الوضع المعزول باستخدام HttpTrigger المخصص كخادم اتصال عن بعد بدون خادم، ما عليك سوى تثبيت:
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" ")>]العضو _.الفهرس ([<HttpTrigger(AuthorizationLevel.Anonymous, Route = "{*any}")>] req: HttpRequestData, ctx: FunctionContext) =Remoting.createApi()|> Remoting.withRouteBuilder FunctionsRouteBuilder.apiPrefix|> Remoting.fromValue myImplementation|> Remoting.buildRequestHandler|> HttpResponseData.fromRequestHandler req
بالطبع، لا يعد وجود تطبيق واحد لكل تطبيق وظيفي أمرًا مثاليًا، لذا فإن HttpResponseData.fromRequestHandlers
هنا للإنقاذ:
اكتب Functions(log:ILogger<Functions>) =[<Function("Index")>] member _.Index ([<HttpTrigger(AuthorizationLevel.Anonymous, Route = "{*any}")>] req: 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 req
قم بتثبيت Fable.Remoting.Client
من nuget باستخدام Paket:
paket add Fable.Remoting.Client --project /path/to/Project.fsproj
قم بالإشارة إلى الأنواع المشتركة لمشروع العميل الخاص بك
<Compile Include="path/to/SharedTypes.fs" />
ابدأ باستخدام المكتبة:
افتح Fable.Remoting.Clientopen SharedTypes// StudentApi: IStudentApilet StudentApi =Remoting.createApi()|> Remoting.buildProxy<IStudentApi>async { // الطلاب: الطالب[] يترك! الطلاب = StudentApi.allStudents() للطلاب في الطلاب do// Student : Studentprintfn "الطالب %s يبلغ من العمر %d عامًا" Student.Name Student.Age}|> Async.StartImmediate
أخيرًا، عندما تستخدم webpack-dev-server
، عليك تغيير التكوين من هذا:
خادم التطوير: { قاعدة المحتوى: حل ('./public')، المنفذ: 8080}
إلى هذا:
خادم التطوير: { قاعدة المحتوى: حل ('./public')، المنفذ: 8080، الوكيل: {'/*': { // أخبر خادم webpack-dev بإعادة توجيه جميع الطلبات من العميل إلى هدف الخادم: "http://localhost:5000"،// بافتراض استضافة خادم الواجهة الخلفية على المنفذ 5000 أثناء تغيير التطويرOrigin: true}}
هذا كل شيء!
يمكنك أيضًا استخدام وظائف العميل في المشروعات غير الخيالية، مثل وحدة التحكم أو سطح المكتب أو تطبيق الهاتف المحمول.
قم بتثبيت Fable.Remoting.DotnetClient
من nuget باستخدام Paket:
paket add Fable.Remoting.DotnetClient --project /path/to/Project.fsproj
قم بالإشارة إلى الأنواع المشتركة في مشروع العميل الخاص بك
<Compile Include="path/to/SharedTypes.fs" />
ابدأ باستخدام المكتبة:
افتح Fable.Remoting.DotnetClientopen SharedTypes// StudentApi: IStudentApilet StudentApi = Remoting.createApi "http://localhost:8085" |> Remoting.buildProxy<IStudentApi>غير متزامن { // الطلاب: الطالب[] يترك! الطلاب = StudentApi.allStudents() للطلاب في الطلاب do// Student : Studentprintfn "الطالب %s يبلغ من العمر %d عامًا" Student.Name Student.Age}|> Async.StartImmediate
لاحظ هنا أنه على عكس عميل Fable، ستحتاج إلى توفير عنوان URL الأساسي للواجهة الخلفية لأنه سيتم نشر عميل dotnet بشكل منفصل عن الواجهة الخلفية.
أضف وظيفة حقل سجل أخرى إلى IStudentApi
تنفيذ تلك الوظيفة
أعد تشغيل الخادم والعميل
منتهي! يمكنك الآن استخدام هذه الوظيفة من العميل أيضًا.
راجع المقالة التالية إذا كنت مهتمًا بكيفية تنفيذ هذه المكتبة (قديمة بعض الشيء ولكنها تمنحك نظرة عامة على الآلية) الاتصال بالخادم والعميل المكتوب بشكل ثابت مع F#: إثبات المفهوم