Fable.Remoting es una capa de comunicación RPC para aplicaciones Fable y .NET, abstrae Http y Json y le permite pensar en sus interacciones cliente-servidor solo en términos de funciones puras sin estado que se verifican estáticamente en tiempo de compilación:
Esta interfaz es un tipo de registro donde cada campo es Async<'T>
o una función que devuelve Async<'T>
escriba ISaludoApi = { saludar: cadena -> Async<cadena>}
let saludoApi = { saludar = nombre divertido ->async { let saludo = sprintf "Hola, %s" nombre devolver saludo}}// Exponer la implementación como un servicelet HTTP webApp = Comunicación remota.createApi() |> Remoting.fromValue saludoApi
// obtener un proxy escrito para el servicelet GreetingApi = Comunicación remota.createApi() |> Remoting.buildProxy<IGreetingApi>// Comience a usar serviceasync { ¡dejar! mensaje = saludoApi.greet "Mundo" printfn mensaje "%s" // Hola, mundo}
Eso es todo, sin HTTP, sin JSON y todo es de tipo seguro.
SAFE-TodoList Una sencilla aplicación de lista de tareas completa (principiante)
tabula-rasa una plataforma de blogs del mundo real (intermedio)
Sistema de reserva de clases de Yobo Yoga implementado con Event Sourcing (avanzado)
La biblioteca se ejecuta en todas partes del backend: como Suave WebPart
, como Giraffe/Saturn HttpHandler
o cualquier otro marco como middleware Asp.NET Core. Los clientes pueden ser aplicaciones Fable o .NET.
"Fable.Remoting resuelve el antiguo problema de mantener el código de front-end sincronizado con el código de backend en el momento de la compilación y en un lenguaje tan divertido de usar como F#" - David Falkner
Utilice la plantilla SAFE Simplificada donde Fable.Remoting ya está configurado y listo para funcionar.
Biblioteca | Versión |
---|---|
Fable.Remoting.MsgPack | |
Fable.Remoting.Cliente | |
Fable.Remoting.Json | |
Servidor.remoto.de.fable | |
Fable.Remoting.Suave | |
Fábula.Remoting.Jirafa | |
Fable.Remoting.AspNetCore | |
Fable.Remoting.DotnetClient | |
Fable.Remoting.AzureFunctions.Worker | |
Fable.Remoting.AwsLambda |
Cree una nueva aplicación de consola de F#:
dotnet new console -lang F#
Defina los tipos que desea compartir entre el cliente y el servidor:
// SharedTypes.fsmodule SharedTypestype Student = {Name: stringAge: int}// Especificaciones compartidas entre el servidor y el tipo de cliente IStudentApi = {studentByName: string -> Async<Student option>allStudents: unidad -> Async<list<Student>>}
El tipo IStudentApi
es muy importante, esta es la especificación del protocolo entre su servidor y cliente. Fable.Remoting
espera que dicho tipo solo tenga funciones que devuelvan Async
en el resultado final:
Asíncrono<A>A -> Asíncrono<B>A -> B -> Asíncrono<C>// etc...
Intente colocar dichos tipos en archivos separados para hacer referencia a estos archivos más adelante desde el Cliente.
Luego proporcione una implementación para IStudentApi
en el servidor:
abrir SharedTypeslet getStudents() = asíncrono {retorno [{ Nombre = "Mike"; Edad = 23; }{ Nombre = "Juan"; Edad = 22; }{ Nombre = "Diana"; Edad = 22; }] }let findStudentByName nombre = asíncrono {¡deja! estudiantes = getStudents()let Student = List.tryFind (estudiante divertido -> Student.Name = nombre) Studentsreturn Student }let StudentApi: IStudentApi = {studentByName = findStudentByName todos los estudiantes = obtener estudiantes}
Ahora que tenemos la implementación studentApi
, puede exponerla como un servicio web desde diferentes marcos web. Empezamos con Suave
Instale la biblioteca desde Nuget usando Paket:
paket add Fable.Remoting.Suave --project /path/to/Project.fsproj
Cree un WebPart a partir del valor studentApi
abrir Suaveopen Fable.Remoting.Serveropen Fable.Remoting.Suavelet webApp: WebPart =Remoting.createApi()|> Remoting.fromValue StudentApi|> Remoting.buildWebPart// iniciar el servidor webstartWebServer defaultConfig webApp
Sí, es así de simple. Puede pensar en el valor webApp
como si fuera lo siguiente en pseudocódigo:
dejar aplicación web = elegir [ POST >=> ruta "/IStudentApi/studentByName" >=> (* deserializar el cuerpo de la solicitud (de json) *) >=> (* invocar StudentApi.getStudentByName con la entrada deserializada *) >=> (* devolver al cliente la salida serializada (a json) *) // otras rutas ]
Puede habilitar el registro de diagnóstico desde Fable.Remoting.Server (recomendado) para ver cómo la biblioteca está haciendo su magia detrás de escena :)
let webApp =Remoting.createApi()|> Remoting.fromValue StudentApi|> Remoting.withDiagnosticsLogger (printfn "%s")|> Remoting.buildWebPart
Instale el paquete de Nuget usando paket
paket add Fable.Remoting.AspNetCore --project /path/to/Project.fsproj
Ahora puede configurar su controlador remoto como middleware AspNetCore
let webApp =Remoting.createApi()|> Remoting.fromValue StudentApilet configureApp (aplicación: IApplicationBuilder) =// Agregar controlador de comunicación remota a la aplicación de canalización ASP.NET Core.UseRemoting webApp[<EntryPoint>]let main _ =WebHostBuilder().UseKestrel ().Configure(Action<IApplicationBuilder> configureApp).Build().Run()0
Puedes seguir la parte de Suave hasta la instalación de la biblioteca, donde se convertirá en:
paket add Fable.Remoting.Giraffe --project /path/to/Project.fsproj
Ahora, en lugar de un WebPart, al abrir el espacio de nombres Fable.Remoting.Giraffe
, obtendrá un HttpHandler del server
de valores:
open Giraffeopen Fable.Remoting.Serveropen Fable.Remoting.Giraffelet webApp: HttpHandler =Remoting.createApi()|> Remoting.fromValue StudentApi|> Remoting.buildHttpHandlerlet configureApp (aplicación: IApplicationBuilder) =// Agregue Giraffe a la aplicación de canalización ASP.NET Core .UseGiraffe webApplet configureServices (servicios: IServiceCollection) =// Agregar Giraffe dependenciesservices.AddGiraffe() |> ignore[<EntryPoint>]let main _ =WebHostBuilder().UseKestrel().Configure(Action<IApplicationBuilder> configureApp).ConfigureServices(configureServices).Build().Run( )0
Puede utilizar la misma webApp
generada por la biblioteca Giraffe.
abra Saturnopen Fable.Remoting.Serveropen Fable.Remoting.Giraffelet webApp: HttpHandler =Remoting.createApi()|> Remoting.fromValue StudentApi|> Remoting.buildHttpHandlerlet aplicación = aplicación {url "http://127.0.0.1:8083/"use_router aplicación web}ejecutar aplicación
Para usar Azure Functions en modo aislado con HttpTrigger personalizado como servidor remoto sin servidor, simplemente instale:
dotnet add package Fable.Remoting.AzureFunctions.Worker
o usando paquete
paket add Fable.Remoting.AzureFunctions.Worker --project /path/to/Project.fsproj
Dado que Azure Functions no sabe nada sobre HttpHandler, necesitamos usar objetos HttpRequestData
y HttpResponseData
integrados. Por suerte, tenemos las funciones Remoting.buildRequestHandler
y HttpResponseData.fromRequestHandler
al rescate:
abra Fable.Remoting.Serveropen Fable.Remoting.AzureFunctions.Workeropen Microsoft.Azure.Functions.Workeropen Microsoft.Azure.Functions.Worker.Httpopen Microsoft.Extensions.Loggingtype Functions(log:ILogger<Funciones>) =[<Función("Índice ")>]miembro _.Index ([<HttpTrigger(AuthorizationLevel.Anonymous, Ruta = "{*cualquiera}")>] solicitud: HttpRequestData, ctx: FunctionContext) =Remoting.createApi()|> Remoting.withRouteBuilder FunctionsRouteBuilder.apiPrefix|> Remoting.fromValue myImplementation|> Remoting.buildRequestHandler|> HttpResponseData.fromRequestHandler solicitud
Por supuesto, tener una implementación por aplicación de función no es lo ideal, por lo que HttpResponseData.fromRequestHandlers
está aquí para ayudar:
escriba Funciones (log:ILogger<Funciones>) =[<Función("Index")>]miembro _.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 ] |> Solicitud HttpResponseData.fromRequestHandlers
Instale Fable.Remoting.Client
desde nuget usando Paket:
paket add Fable.Remoting.Client --project /path/to/Project.fsproj
Haga referencia a los tipos compartidos en el proyecto de su cliente.
<Compile Include="path/to/SharedTypes.fs" />
Comience a usar la biblioteca:
open Fable.Remoting.Clientopen SharedTypes// StudentApi: IStudentApilet StudentApi =Remoting.createApi()|> Remoting.buildProxy<IStudentApi>async { // estudiantes: Estudiante[] ¡dejar! estudiantes = estudianteApi.allStudents() para estudiante en estudiantes do// estudiante: Studentprintfn "El estudiante %s tiene %d años" estudiante.Nombre estudiante.Edad}|> Async.StartImmediate
Finalmente, cuando usas webpack-dev-server
, debes cambiar la configuración de esto:
servidor de desarrollo: { contentBase: resolver('./public'), puerto: 8080}
a esto:
servidor de desarrollo: { contentBase: resolver('./public'), puerto: 8080, proxy: {'/*': { // le dice a webpack-dev-server que redirija todas las solicitudes del cliente al servidor de destino: "http://localhost:5000",// asumiendo que el servidor backend está alojado en el puerto 5000 durante el cambio de desarrollo Origen: verdadero}}
¡Eso es todo!
También puede utilizar la funcionalidad del cliente en proyectos que no son de Fable, como una consola, una aplicación de escritorio o móvil.
Instale Fable.Remoting.DotnetClient
desde nuget usando Paket:
paket add Fable.Remoting.DotnetClient --project /path/to/Project.fsproj
Haga referencia a los tipos compartidos en el proyecto de su cliente.
<Compile Include="path/to/SharedTypes.fs" />
Comience a usar la biblioteca:
abrir Fable.Remoting.DotnetClientopen SharedTypes// StudentApi : IStudentApilet StudentApi = Remoting.createApi "http://localhost:8085" |> Remoting.buildProxy<IStudentApi>async { // estudiantes: Estudiante[] ¡dejar! estudiantes = estudianteApi.allStudents() para estudiante en estudiantes do// estudiante: Studentprintfn "El estudiante %s tiene %d años" estudiante.Nombre estudiante.Edad}|> Async.StartImmediate
Tenga en cuenta aquí que, a diferencia del cliente Fable, deberá proporcionar la URL base del backend porque el cliente dotnet se implementará por separado del backend.
Agregue otra función de campo de registro a IStudentApi
Implementar esa función
Reiniciar servidor y cliente
¡Hecho! Ahora también puedes usar esa función desde el cliente.
Consulte el siguiente artículo si está interesado en cómo se implementa esta biblioteca (un poco desactualizado pero le brinda una descripción general del mecanismo) Comunicación cliente-servidor con tipo estático con F#: prueba de concepto