Fable.Remoting est une couche de communication RPC pour les applications Fable et .NET, elle fait abstraction de Http et Json et vous permet de penser à vos interactions client-serveur uniquement en termes de fonctions pures sans état qui sont vérifiées statiquement au moment de la compilation :
Cette interface est un type d'enregistrement où chaque champ est soit Async<'T>
soit une fonction qui renvoie Async<'T>
tapez IGreetingApi = { saluer : chaîne -> Async<string>}
laissez GreetingApi = { greet = fun name ->async { let Greeting = sprintf "Bonjour, %s" name return Greeting}}// Exposer l'implémentation en tant que servicelet HTTP webApp = Remoting.createApi() |> Remoting.fromValue GreetingApi
// récupère un proxy typé pour le servicelet GreetingApi = Remoting.createApi() |> Remoting.buildProxy<IGreetingApi>// Commencez à utiliser le serviceasync { laisser! message = GreetingApi.greet "Monde" printfn "%s" message // Bonjour tout le monde}
C'est tout, pas de HTTP, pas de JSON et tout est sécurisé.
SAFE-TodoList Une application simple de liste de tâches complète (débutant)
tabula-rasa, une plateforme de blogs réaliste (intermédiaire)
Système de réservation de cours de yoga Yobo mis en œuvre avec Event Sourcing (avancé)
La bibliothèque s'exécute partout sur le backend : comme Suave WebPart
, comme Giraffe/Saturn HttpHandler
ou tout autre framework comme le middleware Asp.NET Core. Les clients peuvent être des applications Fable ou .NET.
"Fable.Remoting résout le problème séculaire de la synchronisation de votre code front-end avec votre code backend au moment de la compilation, et dans un langage aussi agréable à utiliser que F#" - David Falkner
Utilisez le modèle simplifié SAFE où Fable.Remoting est déjà configuré et prêt à fonctionner
Bibliothèque | Version |
---|---|
Fable.Remoting.MsgPack | |
Fable.Remoting.Client | |
Fable.Remoting.Json | |
Fable.Remoting.Serveur | |
Fable.Remoting.Suave | |
Fable.Remoting.Girafe | |
Fable.Remoting.AspNetCore | |
Fable.Remoting.DotnetClient | |
Fable.Remoting.AzureFunctions.Worker | |
Fable.Remoting.AwsLambda |
Créez une nouvelle application console F# :
dotnet new console -lang F#
Définissez les types que vous souhaitez partager entre le client et le serveur :
// SharedTypes.fsmodule SharedTypestype Student = {Name : stringAge : int}// Spécifications partagées entre le serveur et le clienttype IStudentApi = {studentByName : string -> Async<Student option>allStudents : unit -> Async<list<Student>>}
Le type IStudentApi
est très important, c'est la spécification du protocole entre votre serveur et votre client. Fable.Remoting
s'attend à ce qu'un tel type ait uniquement des fonctions renvoyant Async
sur le résultat final :
Async<A>A -> Async<B>A -> B -> Async<C>// etc...
Essayez de placer ces types dans des fichiers séparés pour référencer ces fichiers ultérieurement à partir du client.
Fournissez ensuite une implémentation pour IStudentApi
sur le serveur :
ouvrir SharedTypeslet getStudents() = async {return [{ Nom = "Mike"; Âge = 23 ; }{ Nom = "Jean" ; Âge = 22 ; }{ Nom = " Diane " ; Âge = 22 ; }] } laissez findStudentByName nom = asynchrone {laissez ! Students = getStudents()let student = List.tryFind (étudiant amusant -> student.Name = nom) Studentsreturn student }let studentApi : IStudentApi = {studentByName = findStudentByName tous les étudiants = getStudents}
Maintenant que nous avons l'implémentation studentApi
, vous pouvez l'exposer en tant que service Web à partir de différents frameworks Web. Nous commençons par Suave
Installez la bibliothèque depuis Nuget à l'aide de Paket :
paket add Fable.Remoting.Suave --project /path/to/Project.fsproj
Créer un WebPart à partir de la valeur studentApi
ouvrez Suaveopen Fable.Remoting.Serveropen Fable.Remoting.Suavelet webApp : WebPart =Remoting.createApi()|> Remoting.fromValue studentApi|> Remoting.buildWebPart// démarrer le serveur WebstartWebServer defaultConfig webApp
Oui, c'est aussi simple que cela. Vous pouvez considérer la valeur webApp
comme si elle était la suivante en pseudo-code :
laissez webApp = choisir [ POST >=> chemin "/IStudentApi/studentByName" >=> (* désérialiser le corps de la requête (à partir de json) *) >=> (* invoquez studentApi.getStudentByName avec l'entrée désérialisée *) >=> (* redonner au client la sortie sérialisée (en json) *) // autres itinéraires ]
Vous pouvez activer la journalisation des diagnostics à partir de Fable.Remoting.Server (recommandé) pour voir comment la bibliothèque fait sa magie dans les coulisses :)
let webApp =Remoting.createApi()|> Remoting.fromValue studentApi|> Remoting.withDiagnosticsLogger (printfn "%s")|> Remoting.buildWebPart
Installez le package à partir de Nuget à l'aide de paket
paket add Fable.Remoting.AspNetCore --project /path/to/Project.fsproj
Vous pouvez désormais configurer votre gestionnaire distant en tant que middleware AspNetCore.
let webApp =Remoting.createApi()|> Remoting.fromValue studentApilet configureApp (app : IApplicationBuilder) =// Ajouter un gestionnaire Remoting au pipelineapp ASP.NET Core.UseRemoting webApp[<EntryPoint>]let main _ =WebHostBuilder().UseKestrel ().Configure(Action<IApplicationBuilder> configureApp).Build().Run()0
Vous pouvez suivre la partie Suave jusqu'à l'installation de la bibliothèque, où elle deviendra :
paket add Fable.Remoting.Giraffe --project /path/to/Project.fsproj
Désormais, au lieu d'un WebPart, en ouvrant l'espace de noms Fable.Remoting.Giraffe
, vous obtiendrez un HttpHandler du server
de valeurs :
ouvrez Giraffeopen Fable.Remoting.Serveropen Fable.Remoting.Giraffelet webApp : HttpHandler =Remoting.createApi()|> Remoting.fromValue studentApi|> Remoting.buildHttpHandlerlet configureApp (app : IApplicationBuilder) =// Ajouter Giraffe à l'application pipeline ASP.NET Core .UseGiraffe webApplet configureServices (services : IServiceCollection) =// Ajouter Giraffe dependenciesservices.AddGiraffe() |> ignore[<EntryPoint>]let main _ =WebHostBuilder().UseKestrel().Configure(Action<IApplicationBuilder> configureApp).ConfigureServices(configureServices).Build().Run()0
Vous pouvez utiliser la même webApp
générée par la bibliothèque Giraffe.
ouvrez 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}exécuter l'application
Pour utiliser Azure Functions en mode isolé avec HttpTrigger personnalisé en tant que serveur distant sans serveur, installez simplement :
dotnet add package Fable.Remoting.AzureFunctions.Worker
ou en utilisant un paquet
paket add Fable.Remoting.AzureFunctions.Worker --project /path/to/Project.fsproj
Étant donné qu'Azure Functions ne sait rien de HttpHandler, nous devons utiliser les objets HttpRequestData
et HttpResponseData
intégrés. Heureusement, nous avons les fonctions Remoting.buildRequestHandler
et HttpResponseData.fromRequestHandler
à la rescousse :
ouvrir Fable.Remoting.Serverouvrir Fable.Remoting.AzureFunctions.Workerouvrir Microsoft.Azure.Functions.Workerouvrir Microsoft.Azure.Functions.Worker.Httpopen Microsoft.Extensions.Loggingtype Functions(log:ILogger<Functions>) =[<Function("Index ")>]membre _.Index ([<HttpTrigger(AuthorizationLevel.Anonymous, Route = "{*any}")>] requête : HttpRequestData, ctx : FunctionContext) =Remoting.createApi()|> Remoting.withRouteBuilder FunctionsRouteBuilder.apiPrefix|> Remoting.fromValue myImplementation|> Remoting.buildRequestHandler|> HttpResponseData.fromRequestHandler req
Bien sûr, avoir une seule implémentation par Function App n’est pas idéal, donc HttpResponseData.fromRequestHandlers
est là à la rescousse :
tapez 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 demande
Installez Fable.Remoting.Client
à partir de nuget à l'aide de Paket :
paket add Fable.Remoting.Client --project /path/to/Project.fsproj
Référencez les types partagés à votre projet client
<Compile Include="path/to/SharedTypes.fs" />
Commencez à utiliser la bibliothèque :
ouvrir Fable.Remoting.Clientopen SharedTypes// studentApi : IStudentApilet studentApi =Remoting.createApi()|> Remoting.buildProxy<IStudentApi>async { // étudiants : Étudiant[] laisser! étudiants = studentApi.allStudents() pour l'étudiant dans student do// student : Studentprintfn "L'étudiant %s a %d ans" student.Name student.Age}|> Async.StartImmediate
Enfin, lorsque vous utilisez webpack-dev-server
, vous devez modifier la configuration à partir de ceci :
serveur de développement : { contentBase : résoudre('./public'), port : 8080}
à ceci :
serveur de développement : { contentBase : résoudre('./public'), port : 8080, proxy : {'/*' : { // indique à webpack-dev-server de réacheminer toutes les requêtes du client vers la cible du serveur : "http://localhost:5000",// en supposant que le serveur backend est hébergé sur le port 5000 pendant le changement de développementOrigin: true}}
C'est ça!
Vous pouvez également utiliser les fonctionnalités client dans des projets non fables, tels qu'une console, un ordinateur de bureau ou une application mobile.
Installez Fable.Remoting.DotnetClient
à partir de nuget à l'aide de Paket :
paket add Fable.Remoting.DotnetClient --project /path/to/Project.fsproj
Référencez les types partagés dans votre projet client
<Compile Include="path/to/SharedTypes.fs" />
Commencez à utiliser la bibliothèque :
ouvrir Fable.Remoting.DotnetClientopen SharedTypes// studentApi : IStudentApilet studentApi = Remoting.createApi "http://localhost:8085" |> Remoting.buildProxy<IStudentApi>async { // étudiants : Étudiant[] laisser! étudiants = studentApi.allStudents() pour l'étudiant dans student do// student : Studentprintfn "L'étudiant %s a %d ans" student.Name student.Age}|> Async.StartImmediate
Notez ici que contrairement au client Fable, vous devrez fournir l'URL de base du backend car le client dotnet sera déployé séparément du backend.
Ajouter une autre fonction de champ d'enregistrement à IStudentApi
Implémenter cette fonction
Redémarrer le serveur et le client
Fait! Vous pouvez désormais également utiliser cette fonction depuis le client.
Consultez l'article suivant si vous êtes intéressé par la façon dont cette bibliothèque est implémentée (un peu obsolète mais vous donne un aperçu du mécanisme) Communication client-serveur statiquement typée avec F# : preuve de concept