Jasper purposely eschews the typical
IHandler<T> approach that most .Net messaging frameworks take in favor of a more flexible
model that relies on naming conventions. This might throw some users that are used to being guided by implementing an expected interface
or base class, but it allows Jasper to be much more flexible and reduces code noise.
As an example, here's about the simplest possible handler you could create:
Like most frameworks, Jasper follows the Hollywood Principle where the framework acts as an intermediary
between the rest of the world and your application code. When a Jasper application receives a
MyMessage message through one of its transports, Jasper will call your method and pass in the message that it received.
How Jasper Consumes Your Message Handlers
If you're worried about the performance implications of Jasper calling into your code without any interfaces or base classes, nothing to worry about because Jasper does not use Reflection at runtime to call your actions. Instead, Jasper uses runtime
code generation with Roslyn to write the "glue" code around your actions. Internally, Jasper is generating a subclass of
MessageHandler for each known message type:
See <[linkto:documentation/messaging/handling/handlers]> for information on how Jasper generates the
and how to customize that code.
Out of the box, message handlers need to follow these naming conventions and rules:
- Classes must be public, concrete classes suffixed with either "Handler" or "Consumer"
- Message handling methods must have be public and have a deterministic message type
- The message type has to be a public type
If a candidate method has a single argument, that argument type is assumed to be the message type. Otherwise, Jasper looks for any argument named either "message", "input", or "@event" to be the message type.
See <[linkto:documentation/messaging/handling/discovery]> for more information.
Instance Handler Methods
Handler methods can be instance methods on handler classes if it's desirable to scope the handler object to the message:
Note that you can use either synchronous or asynchronous methods depending on your needs, so you're not constantly being
forced to return
Task.CompletedTask over and over again for operations that are purely CPU-bound (but Jasper itself might be doing
that for you in its generated
Static Handler Methods
As an alternative, you can also use static methods as message handlers:
The handler classes can be static classes as well. This technique gets much more useful when combined with Jasper's support for method injection in a following section.
Jasper can create your message handler objects by using an IoC container (or in the future just use straight up dependency injection
without any IoC container overhead). In that case, you can happily inject dependencies into your message handler classes through the
constructor like this example that takes in a dependency on an
IDocumentSession from Marten:
See <[linkto:documentation/ioc]> for more information about how Jasper integrates the application's IoC container.
Similar to ASP.Net MVC Core, Jasper supports the concept of method injection in handler methods where you can just accept additional arguments that will be passed into your method by Jasper when a new message is being handled.
Below is an example action method that takes in a dependency on an
IDocumentSession from Marten:
So, what can be injected as an argument to your message handler?
- Any service that is registered in your application's IoC container
- The current time in UTC if you have a parameter like
- Services or variables that match a registered code generation strategy. See <[linkto:documentation/middleware_and_codegen]> for more information on this mechanism.
Cascading Messages from Actions
To have additional messages queued up to be sent out when the current message has been successfully completed, you can return the outgoing messages from your handler methods with <[linkto:documentation/messaging/handling/cascading]>.
Using the Message Envelope
To access the
Envelope for the current message being handled in your message handler, just accept
Envelope as a method
argument like this:
See <[linkto:documentation/messaging/publishing/customizing_envelopes]> for more information on interacting with