Fable.Remoting เป็นเลเยอร์การสื่อสาร RPC สำหรับแอป Fable และ .NET ซึ่งจะแยก Http และ Json ออกไป และช่วยให้คุณนึกถึงการโต้ตอบระหว่างไคลเอนต์และเซิร์ฟเวอร์ของคุณเฉพาะในแง่ของฟังก์ชันไร้สถานะล้วนๆ ที่ได้รับการตรวจสอบแบบคงที่ในเวลาคอมไพล์:
อินเทอร์เฟซนี้เป็นประเภทเรกคอร์ดที่แต่ละฟิลด์เป็น Async<'T>
หรือฟังก์ชันที่ส่งคืน Async<'T>
พิมพ์ IGreetingApi = { ทักทาย : string -> Async<string>}
ให้ GreetingApi = { ทักทาย = ชื่อสนุก -> async { ให้คำทักทาย = sprintf "สวัสดี %s" ชื่อส่งคืนคำทักทาย}}// แสดงการใช้งานเป็น HTTP servicelet webApp = การรีโมท.createApi() |> Remoting.fromValue คำอวยพรApi
// รับ typed-proxy สำหรับ servicelet GreetingApi = การรีโมท.createApi() |> Remoting.buildProxy<IGreetingApi>// เริ่มใช้งาน serviceasync { อนุญาต! message = GreetingApi.greet "โลก" ข้อความ printfn "%s" // สวัสดีชาวโลก}
เพียงเท่านี้ ไม่มี HTTP ไม่มี JSON และทุกประเภทปลอดภัย
SAFE-TodoList แอปพลิเคชันรายการสิ่งที่ต้องทำแบบเต็มสแต็กที่เรียบง่าย (เริ่มต้น)
tabula-rasa แพลตฟอร์มบล็อกในโลกแห่งความเป็นจริง (ระดับกลาง)
ระบบจองคลาส Yobo Yoga ใช้งานกับ Event Sourcing (ขั้นสูง)
ไลบรารีทำงานได้ทุกที่บนแบ็กเอนด์: ในฐานะ Suave WebPart
ในฐานะ Giraffe/Saturn HttpHandler
หรือเฟรมเวิร์กอื่น ๆ ในฐานะมิดเดิลแวร์ Asp.NET Core ลูกค้าสามารถเป็นแอปพลิเคชัน Fable หรือ .NET
"Fable.Remoting แก้ปัญหาเก่าแก่ในการทำให้โค้ดส่วนหน้าของคุณซิงค์กับโค้ดแบ็คเอนด์ของคุณ ณ เวลาคอมไพล์ และในภาษาที่สนุกสนานที่จะใช้เป็น F#" - David Falkner
ใช้เทมเพลตแบบง่าย SAFE โดยที่ Fable.Remoting ได้รับการตั้งค่าแล้วและพร้อมใช้งาน
ห้องสมุด | เวอร์ชัน |
---|---|
Fable.Remoting.MsgPack | |
นิทาน.ระยะไกล.ไคลเอนต์ | |
นิทาน.Remoting.Json | |
นิทาน.Remoting.Server | |
นิทาน.ระยะไกล.อ่อนโยน | |
นิทาน.รีโมท.ยีราฟ | |
นิทาน.Remoting.AspNetCore | |
นิทาน.Remoting.DotnetClient | |
Fable.Remoting.AzureFunctions.Worker | |
Fable.Remoting.Awsแลมบ์ดา |
สร้างแอปคอนโซล F# ใหม่:
dotnet new console -lang F#
กำหนดประเภทที่คุณต้องการแชร์ระหว่างไคลเอนต์และเซิร์ฟเวอร์:
// SharedTypes.fsmodule SharedTypestype Student = {Name : stringAge : int}// ข้อกำหนดที่ใช้ร่วมกันระหว่างเซิร์ฟเวอร์และ Clienttype 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() = async {กลับมา [{ ชื่อ = "ไมค์"; อายุ = 23; }{ ชื่อ = "จอห์น"; อายุ = 22; }{ ชื่อ = "ไดอาน่า"; อายุ = 22; - } ให้ findStudentByName name = async {ให้! นักเรียน = getStudents() ให้นักเรียน = List.tryFind (นักเรียนสนุก -> นักเรียน.ชื่อ = ชื่อ) นักเรียนกลับ นักเรียน } ให้ StudentApi : IStudentApi = {studentByName = findStudentByName allStudents = รับนักเรียน}
ตอนนี้เรามีการใช้งาน studentApi
แล้ว คุณสามารถเปิดเผยเป็นบริการเว็บจากกรอบงานเว็บต่างๆ ได้ เราเริ่มต้นด้วยความอ่อนโยน
ติดตั้งไลบรารีจาก Nuget โดยใช้ Paket:
paket add Fable.Remoting.Suave --project /path/to/Project.fsproj
สร้าง WebPart จากค่า studentApi
เปิด Suaveopen Fable.Remoting.Serveropen Fable.Remoting.Suavelet webApp : WebPart =Remoting.createApi()|> Remoting.fromValue StudentApi|> Remoting.buildWebPart// เริ่มเว็บเซิร์ฟเวอร์startWebServer defaultConfig webApp
ใช่ มันง่ายมาก คุณสามารถนึกถึงค่า webApp
เหมือนกับว่าเป็นค่าต่อไปนี้ในโค้ดเทียม:
ให้ webApp = เลือก [ โพสต์ >=> เส้นทาง "/IStudentApi/studentByName" >=> (* ดีซีเรียลไลซ์เนื้อหาคำขอ (จาก json) *) >=> (* เรียกใช้ StudentApi.getStudentByName ด้วยอินพุตดีซีเรียลไลซ์ *) >=> (* ให้เอาต์พุตไคลเอ็นต์กลับมาต่อเนื่องกัน (ถึง json) *) //เส้นทางอื่นๆ -
คุณสามารถเปิดใช้งานการบันทึกการวินิจฉัยได้จาก Fable.Remoting.Server (แนะนำ) เพื่อดูว่าห้องสมุดทำงานอย่างไรเบื้องหลังนั้นช่างมหัศจรรย์ :)
ให้ 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 StudentApilet กำหนดค่า App (แอป : IApplicationBuilder) =// เพิ่มตัวจัดการ Remoting ไปยัง ASP.NET Core ไปป์ไลน์แอพ UseRemoting webApp[<EntryPoint>]let main _ =WebHostBuilder().UseKestrel ().กำหนดค่า(Action<IApplicationBuilder> configurationApp).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 configurationApp (แอป : IApplicationBuilder) =// เพิ่ม Giraffe ให้กับ ASP.NET Core ไปป์ไลน์แอป .UseGiraffe webApplet กำหนดค่าบริการ (บริการ : IServiceCollection) =// เพิ่ม Giraffe dependenciesservices.AddGiraffe() |> ละเว้น[<EntryPoint>]let main _ =WebHostBuilder().UseKestrel().Configure(Action<IApplicationBuilder> configurationApp).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 ไม่รู้อะไรเกี่ยวกับ 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 เส้นทาง = "{*any}")>] คำขอ: HttpRequestData, ctx: FunctionContext) =Remoting.createApi()|> Remoting.withRouteBuilder FunctionsRouteBuilder.apiPrefix|> Remoting.fromValue myImplementation|> Remoting.buildRequestHandler|> คำขอ HttpResponseData.fromRequestHandler
แน่นอนว่าการมีการใช้งานหนึ่งครั้งต่อ Function App นั้นไม่เหมาะ ดังนั้น HttpResponseData.fromRequestHandlers
จึงพร้อมให้ความช่วยเหลือ:
พิมพ์ 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 ให้ handlerTwo =Remoting.createApi()|> Remoting.withRouteBuilder FunctionsRouteBuilder.apiPrefix|> Remoting.fromValue myImplementationTwo|> Remoting.buildRequestHandler [ handlerOne; handlerTwo ] |> คำขอ HttpResponseData.fromRequestHandlers
ติดตั้ง 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// นักเรียน : Studentprintfn "Student %s is %d years old" student.Name Student.Age}|> Async.StartImmediate
สุดท้ายนี้ เมื่อคุณใช้ webpack-dev-server
คุณต้องเปลี่ยนการกำหนดค่าจากสิ่งนี้:
เซิร์ฟเวอร์ผู้พัฒนา: { contentBase: แก้ไข ('./public'), พอร์ต: 8080}
ถึงสิ่งนี้:
เซิร์ฟเวอร์ผู้พัฒนา: { contentBase: แก้ไข ('./public'), พอร์ต: 8080, พร็อกซี: {'/*': { // บอก webpack-dev-server ให้เปลี่ยนเส้นทางคำขอทั้งหมดจากไคลเอนต์ไปยังเซิร์ฟเวอร์เป้าหมาย: "http://localhost:5000",// สมมติว่าเซิร์ฟเวอร์แบ็กเอนด์โฮสต์บนพอร์ต 5,000 ระหว่างการเปลี่ยนแปลงการพัฒนาที่มา: 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>async { // นักเรียน : นักเรียน[] อนุญาต! นักเรียน = StudentApi.allStudents() สำหรับนักเรียนในนักเรียน do// นักเรียน : Studentprintfn "Student %s is %d years old" student.Name Student.Age}|> Async.StartImmediate
โปรดสังเกตที่นี่ ซึ่งไม่เหมือนกับไคลเอ็นต์ Fable คุณจะต้องระบุ Url พื้นฐานของแบ็กเอนด์ เนื่องจากไคลเอ็นต์ dotnet จะถูกใช้งานแยกจากแบ็กเอนด์
เพิ่มฟังก์ชันฟิลด์บันทึกอื่นให้กับ IStudentApi
ใช้งานฟังก์ชั่นนั้น
รีสตาร์ทเซิร์ฟเวอร์และไคลเอนต์
เสร็จแล้ว! ตอนนี้คุณสามารถใช้ฟังก์ชันนั้นจากไคลเอนต์ได้เช่นกัน
ดูบทความต่อไปนี้หากคุณสนใจเกี่ยวกับวิธีการใช้งานไลบรารีนี้ (ล้าสมัยเล็กน้อย แต่ให้ภาพรวมของกลไกแก่คุณ) การสื่อสารระหว่างไคลเอนต์และเซิร์ฟเวอร์ที่พิมพ์แบบคงที่ด้วย F#: Proof of Concept