The first step is to install the FubuMVC.Core nuget. That should bring down dependencies on:
- FubuMVC.Core itself
- StructureMap 4.* (as of 3.0, FubuMVC eliminated its IoC abstractions and only runs on StructureMap)
- Newtonsoft.Json, so watch for binding conflict issues. Sigh.
- HtmlTags 2.1 -- FubuMVC is incompatible with newer versions of HtmlTags
- FubuCore
Web Applications
FubuMVC was originally a framework for building web applications, and that's still the easiest, default way to use FubuMVC. To get started with the classic hello world exercise, start a new class library and add this class below:
public class HomeEndpoint
{
public string Index()
{
return "Hello, world!";
}
}
Based on the default conventions within FubuMVC, the HomeEndpoint.Index()
method will be used to handle
the root "/" url of your web application. The next step would be to bootstrap your new application using the
FubuRuntime
class:
using (var runtime = FubuRuntime.Basic())
{
}
In the code above, we're starting up a basic application with all the defaults for the current Assembly.
The FubuRuntime
is the key class that represents a running FubuMVC application.
Now, to actually exercise this endpoint, we can use FubuMVC's "Scenario" support to write a unit test below:
[Fact]
public void start_and_run()
{
// Bootstrap a basic FubuMVC applications
// with just the default policies and services
using (var runtime = FubuRuntime.Basic())
{
// Execute the home route and verify
// the response
runtime.Scenario(_ =>
{
_.Get.Url("/");
_.StatusCodeShouldBeOk();
_.ContentShouldBe("Hello, World");
});
}
}
Finally, we can start up our hello world application with the NOWIN web server in a self hosted console application like this:
public static class Program
{
public static int Main(string[] args)
{
// Bootstrap the same basic application, but this time
// add NOWIN web server hosting
using (var runtime = FubuRuntime.Basic(_ => _.HostWith<NOWIN>()))
{
Console.WriteLine("Web application available at " + runtime.BaseAddress);
}
return 0;
}
}
Do note that you would need to install the NOWIN nuget separately.
ServiceBus Applications
FubuMVC was originally a framework for web applications and the service bus functionality was added originally in an add on called FubuTransportation and later merged into FubuMVC 3.0. As such, the service bus takes a little more work to bootstrap.
For "hello, world" with the ServiceBus, let's just do a "ping pong" exercise where one running node sends a "Ping" messages to another running node, which sends back a "Pong" message reply to the original node.
First, let's create our two message types and the actual message handler classes that will process the message types:
public class PingMessage
{
}
public class PongMessage
{
}
public class PongHandler
{
public void Handle(PongMessage pong)
{
Console.WriteLine("Received pong message");
}
}
public class PingHandler
{
// This is an example of using "cascading messages"
// The PongMessage returned by this method would
// be sent back to the original sender of the PingMessage
public PongMessage Handle(PingMessage ping)
{
Console.WriteLine("Received ping message");
return new PongMessage();
}
}
So a couple things to note about the code above:
- The message types need to be serializable by whatever serializer that you're using within the service bus
PingHandler
andPongHandler
don't need to implement any kind of FubuMVC interface or base class- FubuMVC will find the handler classes by its type scanning conventions
Now that we've got messages and related handlers, we need to figure out how messages are going to get back an forth via "channels" backed by a "transport" mechanism. FubuMVC's main transport is based on LightningQueueshttps://github.com/LightningQueues/LightningQueues (LQ) and requires the extra FubuMVC.LightningQueues library from Nuget.
The nice thing about the LightningQueues transport is that it doesn't require any installation. All we need to do to enable the LQ backed channels is to configure the queue name and port we want our application to use. Assuming that we'll have two channels we're going to name "Pinger" and "Ponger," we'll build out a Settings class that will hold the addresses of our two channels:
public class HelloWorldSettings
{
public Uri Pinger { get; set; } =
"lq.tcp://localhost:2352/pinger".ToUri();
public Uri Ponger { get; set; } =
"lq.tcp://localhost:2353/ponger".ToUri();
}
After all that, we're finally ready to build out our two applications. The applications are expressed by
a subclass of the FubuTransportRegistry<T>
class, where "T" is the Settings type from above.
public class PingApp : FubuTransportRegistry<HelloWorldSettings>
{
public PingApp()
{
// Configuring PingApp to send PingMessage's
// to the PongApp
Channel(x => x.Ponger)
.AcceptsMessage<PingMessage>();
// Listen for incoming messages from "Pinger"
Channel(x => x.Pinger)
.ReadIncoming();
}
}
public class PongApp : FubuTransportRegistry<HelloWorldSettings>
{
// Listen for incoming messages from "Ponger"
public PongApp()
{
Channel(x => x.Ponger)
.ReadIncoming();
}
}
Finally, we can spin up both the "Ping" and "Pong" applications and send messages from one to the other:
public class HelloWorld
{
[Fact]
public async Task send_and_receive()
{
// Spin up the two applications
var pinger = FubuRuntime.For<PingApp>();
var ponger = FubuRuntime.For<PongApp>();
var bus = pinger.Get<IServiceBus>();
// This sends a PingMessage and waits for a corresponding
// PongMessage reply
var pong = await bus.Request<PongMessage>(new PingMessage());
pong.ShouldNotBe(null);
pinger.Dispose();
ponger.Dispose();
}
}