shmew / fable.signalr Goto Github PK
View Code? Open in Web Editor NEWA functional type-safe wrapper for SignalR and Fable.
Home Page: https://shmew.github.io/Fable.SignalR/
License: MIT License
A functional type-safe wrapper for SignalR and Fable.
Home Page: https://shmew.github.io/Fable.SignalR/
License: MIT License
When following the counter example outlined in the documentation everything works fine with the dotnet client but the fable client throws a 405 error when negotiating the connection. This is because it sends an options request and not a get request. I got it working by disabling the negotiation phase.
This is somewhat unfortunate since the microsoft documentation tells you that you get this error because you dont have cors enabled and sends you down a different rabbit hole: https://docs.microsoft.com/en-us/aspnet/core/signalr/troubleshoot?view=aspnetcore-6.0
Response code 405
Http status code 405 - Method Not Allowed
The app doesn't have CORS enabled
I think that it would be nice if the library was split into two parts:
@microsoft/signalr
), without any additional idiomatic F# features, but with types.Fable.Import.SignalR
.Fable.SignalR
name in this case is a good idea, but I have no suggestions yet.My point is to provide also just Fable bindings if someone needs it, but doesn't need the whole set of library functions.
Yes. Trying to add Fable.SignalR.Saturn (and therefore .AspNetCore) with Saturn 0.16.x / Giraffe 6.x doesn't work:
- Fable.SignalR.AspNetCore 0.11.5 requested package System.Text.Encodings.Web: >= 5.0 < 6.0
- System.Text.Json 6.0.2 requested package System.Text.Encodings.Web: >= 6.0
- Microsoft.ApplicationInsights.Profiler.Core 2.3.1 requested package system.text.encodings.web: >= 5.0.1
If System.Text.Encodings.Web supported 6.x as well, this issue might go away (I suspect that there may be other deps that also need promoting).
Downgrading our project to .NET 5, but this would be highly undesirable.
I am using azure signalr services...
Only need to register the UI, not sure how to go about it. I keep getting a warning saying "the client method blahblabh not found"
In the Femto section on the installation page in the docs it says "Do note that this will not install the optional dependencies listed above (the babel plugin and prettier)."
I don't see these listed above. Am I missing something?
PS E:\Webdev\Fable\CFR-In-Fable\src\Server> paket add Fable.SignalR.Elmish
Paket version 7.2.1+8e4eb74b42fbd45f39f7afce9184c16ebb65f16c
Adding package 'Fable.SignalR.Elmish'
Resolving dependency graph...
Conflict detected:
- Fable.SignalR.Elmish 0.11.5 requested package Fable.Elmish: >= 3.1 < 4.0
- Elmish.Bridge.Client 7.0.2 requested package Fable.Elmish: >= 4.0
- Fable.Elmish.React 4.0 requested package Fable.Elmish: >= 4.0
At this point the library is out of date and needs upgrading.
Fable.FastCheck (0.33)
Fable.Core (>= 3.2.8 < 4.0)
Fable.Elmish (>= 3.1 < 4.0)
Fable.Promise (>= 2.2.2 < 3.0)
FSharp.Core (>= 4.7.2)
In particular the library that is blocking us here is this one. Since FastCheck is just for testing, would it be possible to omit it?
Could you give the full demo of using SignalR with Elmish?
I can't understand the example (https://shmew.github.io/Fable.SignalR/#/signalr-client/streaming-server), what does mean 'Subscription' there
| StartServerStream ->
let subscriber dispatch =
{ next = SignalRStreamMsg >> dispatch
complete = fun () -> StreamStatus.Finished |> StreamStatus |> dispatch
error = StreamStatus.Error >> StreamStatus >> dispatch }
{ model with StreamStatus = StreamStatus.Streaming }
, Cmd.SignalR.streamFrom model.Hub StreamFrom.Action.GenInts **Subscription** subscriber
How to get this object?
In the Authorization documentation here:
https://shmew.github.io/Fable.SignalR/#/authorization
It says for ASP.NET Core/Giraffe you can use the SignalR.ConfigBuilder, however the example code is invalid. The "Build" method at the end of the DSL chain is internal in the ConfigBuilder type, so ConfigBuilder doesn't seem to be usable.
Just use the example from the documentation in a project that isn't considered internal to the libraries.
Build to be public so it can be called and ConfigBuilder used.
This project needs Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson 5.0.7 yet in NuGet the package ranges are (>= 5.0.0 && < 6.0.0).
When using NuGet client this causes runtime errors as NuGet defaults to 5.0.0 for this transitive when it needs at least 5.0.7.
Given following dependencies:
source https://api.nuget.org/v3/index.json
framework: net5.0
storage: none
nuget Fable.Remoting.Giraffe
nuget Saturn
nuget Expecto
nuget Fable.FontAwesome.Free
nuget Fable.Mocha
nuget Fable.Remoting.Client
nuget Fable.SignalR.Saturn
nuget Fable.SignalR.Feliz
nuget Feliz.Bulma
nuget Fulma
nuget Fake.Core.ReleaseNotes
nuget Fake.Core.Target
nuget Fake.DotNet.Cli
nuget Farmer
when I'm trying to do paket update, it fails to find a valid resolution due to:
Paket may still find a valid resolution, but this might take a while.
Conflict detected:
- Fable.SignalR.AspNetCore 0.11.2 requested package Fable.Remoting.MsgPack: >= 1.6.2 < 1.7
- Fable.Remoting.Client 7.6.0 requested package Fable.Remoting.MsgPack: >= 1.8
- Fable.SignalR 0.11.2 requested package Fable.Remoting.MsgPack: >= 1.6.2 < 2.0
I think this is due to the project having a ~>
pinned dependency on locked version of Fable.Remoting.MsgPack, hence in nuspec it ends up as < 1.7
If there's no breaking change in that dependency, then possibly loosening that restriction could help fix the issue.
As a workaround I could try defining separate paket groups for Client and Server dependencies, but I'd rather have a single group unless there's a good reason not to.
This relates to the .NET client - I am not sure if it affects other clients.
IHubConnection<...>
has a StreamFrom
that accepts a CancellationToken
and forwards onto the SignalR hub's StreamAsync
method, but HubConnection<...>.StreamFrom
does not likewise accept a CancellationToken
. This means clients cannot cancel streams.
Cancellation should be exposed to clients, allowing them to proactively cancel a stream.
I've hacked this into my own copy of the code to prove it would work how I'd expect (how a raw SignalR stream works, basically). It required changes to both client and server.
Apologies for a noob question but I can't get my head around following:
In the sample code you're sending request to increment counter from client, the counter is incremented on server, and new counter is sent back to client in response and then set inside React Component state:
let render = React.functionComponent(fun () ->
let count,setCount = React.useState 0
let hub =
React.useSignalR<Action,Response>(fun hub ->
hub.withUrl(Endpoints.Root)
.withAutomaticReconnect()
.configureLogging(LogLevel.Debug)
.onMessage <|
function
| Response.NewCount i -> setCount i
| Response.RandomCharacter str -> setText str
)
now say I'd like to capture current count state when handling the message, e.g. do increment on client side:
// ignore counter from server and try using current state - but count is always 0
| Response.NewCount _ -> setCount (count + 1)
But this approach doesn't work - count
evaluates always to 0.
The approach works fine with bare state hook:
[<ReactComponent>]
let Counter () =
let x, setX = React.useState 0
Html.div [
Html.h1 (string x)
Html.button [
// works as expected
prop.onClick (fun _ -> setX (x + 1))
]
]
Is that by design? How can I work around that?
" ./.fable/Fable.SignalR.0.4.0/HubConnection.fs (306,50) error FSHARP: The namespace or module 'Http' is not defined. Maybe you want one of the following: "
So maybe some package is missing?
paket dependencies forthe client:
group Client
FSharp.Core
Fable.Core
Fable.Elmish.Debugger
Fable.Elmish.React
Fable.Elmish.HMR
Thoth.Fetch
Fulma
Fable.FontAwesome.Free
Fable.SignalR.Elmish
Calling multiple server-to-client streams at once in a React useEffect
appears to drop all but only the first message. Likely an issue with the protocol implementation.
This could reasonably be considered a bug, but I'm submitting as a feature request...
The only CancellationToken
available to server stream implementations is the one inside FableHub<...>.HubContext.ConnectionAborted
. This CT is provided by SignalR and exposed by Fable.SignalR. However, it is not sufficient for a server stream to base its life time on this CT because it only triggers if the connection itself is severed. It is not triggered in the event of a client explicitly cancelling a stream like this:
let cancellationTokenSource = new CancellationTokenSource();
let stream = hubConnection.StreamAsync<int>("Counter", 10, 500, cancellationTokenSource.Token)
// later
cancellationTokenSource.Cancel() |> ignore
The requirement for that to work is that the hub's streaming method must include a CancellationToken
parameter, but Fable.SignalR's hubs do not declare this parameter.
See the docs on streaming for more info.
Streaming methods in the various hubs should include a CancellationToken
parameter that is then passed into the stream implementation. Unfortunately, this means that the streaming function signature will need to change to include the CT. This will be a breaking change, but unavoidable as far as I can tell.
The only alternative would be to add a property to the FableHub
interface, but that would mean:
StreamCancelled
FableHub
rather than the HubContext
, so it would feel a bit weirdTo me, it's cleaner and clearer to change the existing function signature, making it clear to implementations that they really should care about that CT and not ignore it.
Hi, first, thanks for this library, great work and documentation.
How do I set the target
of a message? I've been through the source code, and got myself a little lost.
Following the docs, I can get a message like the one on the right in the screenshot below, but my server is expecting a message in the format on the left. We'll be changing the protocol once we have a PoC working, this is a learning phase. Thank you!
I'm using Elmish, but can drop back to native if it gives me access to a richer/more flexible API.
Thanks
From the docs:
type Model =
{ Count: int
Text: string
Hub: Elmish.Hub<Action,Response> option }
// This only works if you're using Feliz.UseElmish or Feliz.ElmishComponents!
//
// If you're using the traditional elmish model you will need to clean this up yourself.
interface System.IDisposable with
member this.Dispose () =
this.Hub |> Option.iter (fun hub -> hub.Dispose())
What does mean? When do I have to dispose the hub? Every time I create a new model? Or only once at the end of my application?
I get following compile error:
error NU1202: Package Microsoft.AspNetCore.Authentication.JwtBearer 5.0.1 is not compatible with netcoreapp3.1 (.NETCoreApp,Version=v3.1). Package Microsoft.AspNetCore.Authentication.JwtBearer 5.0.1 supports: net5.0 (.NETCoreApp,Version=v5.0)
dotnet fake build -t run
Produces the shown error.
I'm not sure. I'm pretty new to all this Fable/F#/netcore stuff. As far as I understand the SAFE template uses netcoreapp3.1 and it looks like Fable.SignalR adds a dependency (via Microsoft.AspNetCore.Authentication.JwtBearer) to net5.0.
So, there is no way to use Fable.SignalR with netcoreapp3.1?
Or could I try to install an older version? If so, which one?
I would be grateful for any advice. If it's not a Fable.SignalR problem, please let me know. It's very confusing for a newbie to see who is responsible for which package etc.
When handling a message how do I get hold of the actual hub so I can send a message? The simple counter example only updates when receiving data, it doesn't ever send anything back from those handlers as I can see.
My first naive approach was this:
let hub =
React.useSignalR<Shared.Command, Shared.Event>(fun hub ->
hubBuilder.withUrl(sprintf "http://localhost:5000%s" Endpoints.Root)
.withAutomaticReconnect()
.configureLogging(LogLevel.Debug)
.onMessage (handleEvent hub)
When I started implementing handleEvent
I realized I just had access to the hub builder, not the actual hub. So what would be the recommended way of getting access to the hub in handleEvent
?
I tried to use Fable.SignalR in my project with the steps given in the docs, but I am running into compile errors.
I've added Fable.SignalR, SignalR.Elmish, SignalR.Feliz. I've even tried to add SimpleHttp...
.fable/Fable.SignalR.0.11.5/HubConnection.fs(332,46): (332,50) error FSHARP: The namespace or module 'Http' is not defined. Maybe you want one of the following: HttpClient (code 39)
Enable users to call Fable.SignalR hubs from the .NET client.
Reporting this because it took me a couple hours to figure out.
One must be sure to invoke the same AddSignalR
and UseSignalR
overloads, otherwise confusing dependency resolution errors occur. For example, if one invokes:
services.AddSignalR(signalRSettings, streamToClient)
// Oops, forgot to pass in streamToClient
app.UseSignalR(signalRSettings)
The following runtime error occurs when attempting to connect to the hub:
fail: Microsoft.AspNetCore.SignalR.HubConnectionHandler[1]
Error when dispatching 'OnConnectedAsync' on hub.
System.InvalidOperationException: Unable to resolve service for type 'Fable.SignalR.BaseFableHubOptions`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]' while attempting to activate 'Fable.SignalR.BaseFableHub`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]'.
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method(Closure , IServiceProvider , Object[] )
at Microsoft.AspNetCore.SignalR.Internal.DefaultHubActivator`1.Create()
at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnConnectedAsync(HubConnectionContext connection)
at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnConnectedAsync(HubConnectionContext connection)
at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.RunHubAsync(HubConnectionContext connection)
AddSignalR
and UseSignalR
overloads, which causes a mismatch between types registered and types requested during resolutionAt minimum, it would be nice to have a good runtime error message guide the dev here. But I'm also not sure why the split between send/invoke and streaming exists. If Settings
had streaming-related properties in it too, there would be no need for multiple overloads and hence no chance of mismatching calls.
Message RegisterHub
lets me access hub.hub.state
to find the initial connection status (so far that's been "Connecting"), and the connection builder lets me register OnReconnecting
and OnReconnected
handlers. What's the right way for me to detect the initial connection [event]?
https://shmew.github.io/Fable.SignalR/#/signalr-client/api#hubconnectionbuilder
Thank you!
Hi,
is it possible to broadcast messages to clients from my REST API?
E.g.:
My problem is, that I don't have access to the hub context. So I'm not able to invoke
let send (msg: Action) (hubContext: FableHub<Action, Response>) = ...
from inside my REST API.
My server currently uses the Saturn framework. Like this:
let app =
application {
use_signalr (
configure_signalr {
endpoint Blizzard.Endpoints.Root
send SignalRHub.send
invoke SignalRHub.invoke
}
)
url "http://0.0.0.0:8085"
use_router webApp
memory_cache
use_static "public"
use_gzip
}
When searching for mixing REST and SignalR with ASPNetCore the solution looks like this:
https://stackoverflow.com/a/12369683/2356048
The point is, that there is a global static class GlobalHost
where the hub context can be retrieved.
Is there a way to do something similar?
I followed the basic setup in the docs, but when the client attempts to connect to the hub, the api throws the following exception:
Microsoft.AspNetCore.SignalR.HubConnectionHandler[1]
Error when dispatching 'OnConnectedAsync' on hub.
System.InvalidOperationException: Unable to resolve service for type 'Fable.SignalR.BaseFableHubOptions`2[Api.SignalRHub+SignalRAction,Api.SignalRHub+Response]' while attempting to activate 'Fable.SignalR.BaseFableHub`2[Api.SignalRHub+SignalRAction,Api.SignalRHub+Response]'.
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method44(Closure , IServiceProvider , Object[] )
at Microsoft.AspNetCore.SignalR.Internal.DefaultHubActivator`1.Create()
at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnConnectedAsync(HubConnectionContext connection)
at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnConnectedAsync(HubConnectionContext connection)
at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.RunHubAsync(HubConnectionContext connection)
Appreciate any help!
Hi,
is it possible to broadcast messages to clients from my REST API?
E.g.:
My problem is, that I don't have access to the hub context. So I'm not able to invoke
let send (msg: Action) (hubContext: FableHub<Action, Response>) = ...
from inside my REST API.
My server currently uses the Saturn framework. Like this:
let app =
application {
use_signalr (
configure_signalr {
endpoint Blizzard.Endpoints.Root
send SignalRHub.send
invoke SignalRHub.invoke
}
)
url "http://0.0.0.0:8085"
use_router webApp
memory_cache
use_static "public"
use_gzip
}
When searching for mixing REST and SignalR with ASPNetCore the solution looks like this:
https://stackoverflow.com/a/12369683/2356048
The point is, that there is a global static class GlobalHost
where the hub context can be retrieved.
Is there a way to do something similar?
Hello,
Thanks for this library. First time user here, so apologies if I'm asking something dumb.
I was trying to determine from the docs whether the client library requires the use of the server library, or whether it could integrate with an existing SignalR backend.
Context: I already have a SignalR stream API that is using a channel rather than async enumerable. It doesn't seem as straightforward as I'd hoped to convert from a channel-based implementation to one based on async enumerable, and if I was to convert my backend, Fable.SignalR seems to only support the latter. So that got me wondering whether I could just integrate directly.
Fable.Lit requires Fable.Promise >= 3.1.0, Fable.SignalR <=3.0.0
Hey, thanks for this project! I love the neat, functional interface over SignalR.
I want to use the server-side bindings with a native app not made in .NET or in Fable. I've set up a small hub with Giraffe, and I'm able to connect to it with Postman but my initial handshake isn't going through. I know (for example) Fable.Remoting allows raw HTTP connections and has a small guide to it, so is something like that possible with Fable.SignalR? If so, is there something I'd need to set up to get the WS working?
Here's what I get when I try to send a handshake to the hub:
The NormalFableHub
isn't marked with the [<Authorize>]
attribute, so the Context.User
fields are empty.
hub.wuthUrl("url", fun conn -> conn.accessTokenFactory(fun() -> TOKEN)
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(configureJwt))
hub.Context.User
fieldsI'm not entirely sure if this is the problem, but I think it makes sense.
Hello. I have successfully managed to get the Dotnet.Elmish client working as expected, but I can't seem to get the normal Dotnet client working. Invoking an action on the hub seems to work, but Sending an action doesn't. As far as I can tell, the request is successfully sent from the client, but the server never does anything with it.
Additionally, I can't seem to get Message Pack working either. The client always thinks that the server is using plain json serialization.
I've attached a minimal demo project for these problems. https://github.com/kaeedo/FableSignalRDotnetMinimal
I fear I'm missing some simple configuration, but since it works as expected when using the dotnet.elmish client, I thought I'd ask anyway.
Thanks.
When I execute fake build
it throws an error:
Script reported an error:
-> BuildFailedException: Target 'YarnInstall' failed.
-> One or more errors occurred. (Start of process '/home/prunkles/Develop/repos/Shmew/Fable.SignalR/packages/tooling/Yarnpkg.Yarn/content/bin/yarn.cmd' failed.)
-> Start of process '/home/prunkles/Develop/repos/Shmew/Fable.SignalR/packages/tooling/Yarnpkg.Yarn/content/bin/yarn.cmd' failed.
-> Win32Exception: Permission denied
fake build
Successful compilation of the project.
OS: Manjaro Linux x86_64
Kernel: 4.19.126-1-MANJARO
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.