Communicating With the Server
Remoting
The simplest way to implement client-server communication with WebSharper is by remote method calls.
Add the [Remote]
attibute on a static method and you will be able to call it from
JavaScript-translated code, all serialization will be handled automatically by the WebSharper runtime.
Customization of the remoting mechanism are possible for both the server and client side.
Remote calls can fall into three categories, depending on their return type.
Send message
Remote methods returning void
are sending data to the server without blocking on the client.
No callback is registered so there is no way to confirm success or error state.
Synchronous request
Synchronous remote calls are possible, but not recommended, as they are blocking JavaScript execution on the client. They give a deprecated warning with most current JavaScript engines.
Asynchronous request
Remote methods with return types Task
or Task<T>
(or FSharpAsync
) allow waiting on the result asynchronously.
Here is an example of an RPC call which runs a database query asynchronously on the server:
public static class Server
{
[Remote]
public static async Task<string[]> GetTitlesByAuthor(string author)
{
{
using (var ctx = new MyDataContext())
{
return await (
from a in ctx.Articles
where a.Author.Name = author
select a.Title).ToArrayAsync();
}
}
}
}
[JavaScript]
public class SomeClientSideClass
{
public async Task DisplayTitles(string author)
{
var titles = await Server.GetTitlesByAuthor(author);
// ...
}
}
Here await
on the client-side code means that execution of the rest of the async
method will be resumed by a callback after the server has responded.
Task based remoting also propagates errors produced by the underlying XMLHttpRequest
as exceptions, such as the server responding with an error code.
Features
Supported types
Remote method arguments are serialized from/to JSON. All types occurring in method parameters and return must be one of the following:
System
namespace numeric types,string
,bool
,DateTime
,TimeSpan
,Guid
,Nullable<T>
,Tuple<...>
. Usingdecimal
on the client side needs theWebSharper.MathJS
package referenced.- Enums.
- Collection types:
Array<T>
(one-dimensional arrays),List<T>
,Queue<T>
,Stack<T>
,LinkedList<T>
. Other collection types have to be converted to a supported one. - Classes with a default constructor for which the values of all fields are themselves serializable
or marked with the
NonSerialized
attribute. TheSerializable
attribute is not required and does not affect behavior. - F# union and record types (including those defined in
FSharp.Core
),FSharpSet<>
,FSharpMap<_>
.
Security
Remote methods are exposed as http endpoints, so any security measures have to be integrated into the method body itself.
WebSharper.Web.Remoting.GetContext().UserSession
exposes some utilities for tracking users.
This uses System.Web.Security.FormsAuthentication
on the server and cookies in the browser. See here for more information.
//using WebSharper.Web;
[Remote]
public static async Task<bool> Login(string user, string password)
{
var ctx = WebSharper.Web.Remoting.GetContext();
if (await VerifyLogin(user, password)) {
await ctx.UserSession.LoginUserAsync(user);
return true;
}
return false;
}
Server-side customization
You can also use instance methods for client-server communication. The syntax for this is:
//using static WebSharper.JavaScript.Pervasives;
Remote<MyType>().MyMethod(...)
The method invoked should be annotated with the Remote
attribute and
follows the same convention as static methods:
public class MyType
{
[<Remote>]
public a this.MyMethod(...) = //...
}
In this case, you must provide a single instance of each type you use, preferably when the web application starts (such as in Global.asax
).
WebSharper.Core.Remoting.AddHandler(typeof(MyType), new MyType());
Note: on ASP.Net Core, instead of calling WebSharper.Core.Remoting.AddHandler
, you can add the handler to the dependency injection graph using services.AddWebSharperRemoting<MyType>()
. See here for more details.
Remote annotated methods can be abstract or virtual.
The instance that you provide by AddHandler
can be of a subclass of the type in the first argument.
This way you can have parts of server-side RPC functionality undefined in a library, and have them implemented in your web project for better separation of concerns.
// Common server and client code:
public abstract class MyType
{
[Remote]
public abstract ... MyMethod(...);
}
// Client consumer:
Remote<MyType>().MyMethod(...)
// Server implementation:
public class MyTypeImpl
{
public ... MyMethod(...) { ... }
}
// App startup:
WebSharper.Core.Remoting.AddHandler(typeof(MyType), new MyTypeImpl());
Client-side customization
If you want to change the URL that remoting calls target by default, set the value at the WebSharper.Remoting.EndPoint
property at the startup of your client-side code.
You can You can further customize how RPC methods are called from the client-side code by writing a class implementing the
customize how RPC methods are called from the client-side code by writing a class implementing the
WebSharper.Remoting.IRemotingProvider
interface, then adding a RemotingProvider
attribute on the remote method
with the custom remoting provider type as argument.
The IRemotingProvider
provider has 4 methods for implementing synchronous, asynchronous (Task and F# Async), and send calls: Sync
, Task
, Async
, Send
respectively.
You have to implement only the methods that your RPC methods will be using depending on their signatures.
The RemotingProvider
attribute can be also used on classes and assemblies, overriding client-side remote handling for
all RPC methods found in their scope.
The default implementation used is WebSharper.Remoting.AjaxRemotingProvider
.
Communication Protocol
The communication protocol used by WebSharper is a custom protocol
built on top of HTTP and JSON. The client sends HTTP POST requests
marked with a special HTTP header x-websharper-rpc
to the current
URL of the page (?
), with the bodies of the requests containing the
JSON-serialized method arguments. The server responds with a JSON
reply.
The URL to which the requests are sent can be customized modifying the value of WebSharper.Remoting.EndPoint
static property on client-side, the default implementation of IRemotingProvider
uses this string value.