Fable.Remoting — это уровень связи RPC для приложений Fable и .NET. Он абстрагирует Http и Json и позволяет вам думать о взаимодействии клиент-сервер только с точки зрения чистых функций без сохранения состояния, которые статически проверяются во время компиляции:
Этот интерфейс представляет собой тип записи, где каждое поле является либо Async<'T>
, либо функцией, возвращающей Async<'T>
введите IGreetingApi = { приветствие: строка -> Async<строка>}
пусть приветствиеApi = { Greeting = fun name ->async { let Greeting = sprintf "Hello, %s" name return Greeting}}// Представляем реализацию как HTTP-сервислет webApp = Удаленное взаимодействие.createApi() |> Remoting.fromValue GreetingApi
// получаем типизированный прокси для сервислета GreetingApi = Удаленное взаимодействие.createApi() |> Remoting.buildProxy<IGreetingApi>// Начинаем использовать serviceasync { позволять! message = GreetingApi.greet "Мир" printfn Сообщение "%s" // Привет, мир}
Вот и все: никакого HTTP, никакого JSON, и все это типобезопасно.
SAFE-TodoList Простое полнофункциональное приложение со списком дел (для начинающих)
tabula-rasa — реальная платформа для блогов (средний уровень)
Система бронирования занятий йобо йогой, реализованная с помощью Event Sourcing (расширенная версия)
Библиотека работает повсюду на серверной части: как Suave WebPart
, как Giraffe/Saturn HttpHandler
или любая другая платформа, как промежуточное программное обеспечение Asp.NET Core. Клиентами могут быть приложения Fable или .NET.
«Fable.Remoting решает извечную проблему синхронизации вашего клиентского кода с серверным кодом во время компиляции и на таком же приятном в использовании языке, как F #» — Дэвид Фолкнер
Используйте шаблон SAFE Simplified, где Fable.Remoting уже настроен и готов к работе.
Библиотека | Версия |
---|---|
Fable.Remoting.MsgPack | |
Fable.Remoting.Client | |
Fable.Remoting.Json | |
Fable.Remoting.Server | |
Fable.Remoting.Обходительный | |
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}// Общие спецификации между сервером и клиентом. Тип IStudentApi = {studentByName: string -> Async<Student option>allStudents: unit -> Async<list<Student>>}
Тип IStudentApi
очень важен, это спецификация протокола между вашим сервером и клиентом. Fable.Remoting
ожидает, что у такого типа будут только функции, возвращающие Async
конечный результат:
Async<A>A -> Async<B>A -> B -> Async<C>// и т.д...
Попробуйте поместить такие типы в отдельные файлы, чтобы позже ссылаться на эти файлы из Клиента.
Затем предоставьте реализацию IStudentApi
на сервере:
открыть SharedTypeslet getStudents() = асинхронный {return [{ Name = "Майк"; Возраст = 23; }{ Имя = "Джон"; Возраст = 22; }{ Name = "Диана"; Возраст = 22; }] } пусть findStudentByName name = асинхронный {пусть! студенты = getStudents()let Student = List.tryFind (веселой студент -> Student.Name = имя) студенты возвращают студента }let StudentApi: IStudentApi = {studentByName = findStudentByName allStudents = getStudents}
Теперь, когда у нас есть реализация studentApi
, вы можете представить ее как веб-сервис из разных веб-фреймворков. Начнем с учтивости
Установите библиотеку из 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 add Fable.Remoting.AspNetCore --project /path/to/Project.fsproj
Теперь вы можете настроить свой удаленный обработчик как промежуточное программное обеспечение AspNetCore.
let webApp =Remoting.createApi()|> Remoting.fromValue StudentApilet configureApp (app: IApplicationBuilder) =// Добавить обработчик удаленного взаимодействия в конвейер ASP.NET Coreapp.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
, вы получите HttpHandler от server
значений:
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 (services: IServiceCollection) =// Добавить зависимости Services.AddGiraffe() |> ignore[<EntryPoint>]let main _ =WebHostBuilder().UseKestrel().Configure(Action<IApplicationBuilder> configureApp).ConfigureServices(configureServices). Построить().Выполнить()0
Вы можете использовать то же webApp
созданное библиотекой Giraffe.
открыть 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}запустить приложение
Чтобы использовать функции Azure в изолированном режиме с пользовательским HttpTrigger в качестве бессерверного сервера удаленного взаимодействия, просто установите:
dotnet add package Fable.Remoting.AzureFunctions.Worker
или с помощью пакета
paket add Fable.Remoting.AzureFunctions.Worker --project /path/to/Project.fsproj
Поскольку функции Azure ничего не знают о HttpHandler, нам нужно использовать встроенные объекты HttpRequestData
и HttpResponseData
. К счастью, у нас есть функции Remoting.buildRequestHandler
и HttpResponseData.fromRequestHandler
:
open 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 ")>]член _.Index ([<HttpTrigger(AuthorizationLevel.Anonymous, Route = "{*any}")>] req: HttpRequestData, ctx: FunctionContext) =Remoting.createApi()|> Remoting.withRouteBuilder FunctionsRouteBuilder.apiPrefix|> Remoting.fromValue myImplementation|> Удаленное взаимодействие.buildRequestHandler|> HttpResponseData.fromRequestHandler запрос
Конечно, наличие одной реализации для каждого приложения-функции не является идеальным, поэтому на помощь придет HttpResponseData.fromRequestHandlers
:
type 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" />
Начните использовать библиотеку:
open 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
, вам необходимо изменить конфигурацию следующим образом:
сервер разработчика: { contentBase: разрешить('./public'), порт: 8080}
на это:
сервер разработчика: { contentBase: разрешить('./public'), порт: 8080, proxy: {'/*': { // сообщаем webpack-dev-serverу перенаправить все запросы от клиента на сервер target: "http://localhost:5000",// при условии, что внутренний сервер размещен на порту 5000 во время изменения разработкиOrigin: true}}
Вот и все!
Вы также можете использовать клиентские функции в проектах, не относящихся к Fable, таких как консоль, настольное или мобильное приложение.
Установите 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>async { // студенты: Студент[] позволять! студенты = StudentApi.allStudents() для студента в студентах do// Student : Studentprintfn "Студенту %s исполнилось %d лет" Student.Name Student.Age}|> Async.StartImmediate
Обратите внимание, что в отличие от клиента Fable вам потребуется указать базовый URL-адрес серверной части, поскольку клиент dotnet будет развернут отдельно от серверной части.
Добавьте еще одну функцию поля записи в IStudentApi
Реализовать эту функцию
Перезагрузите сервер и клиент
Сделанный! Теперь вы также можете использовать эту функцию из клиента.
См. следующую статью, если вам интересно, как реализована эта библиотека (немного устаревшая, но дает вам обзор механизма). Статически типизированная связь клиент-сервер с F #: доказательство концепции.