Plans and Intents
Unfiltered conditionally handles incoming requests using partial functions. From the application’s perspective, requests are mapped to code paths by pattern matching. The library uses a particular vocabulary to refer to the agents of this process without ambiguity.
- An intent is a partial function for matching requests.
- A plan binds an intent to a particular server interface.
For example, the unfiltered.filter.Plan
trait extends the jakarta.servlet.Filter
interface. It declares an abstract intent
method for clients to define the intent partial function.
Making Plans of Intents
Looking back at the example on the previous page, you might wonder where the plan ends and the intent begins.
sourceimport unfiltered.request._
import unfiltered.response._
val echo = unfiltered.filter.Planify {
case Path(Seg(p :: Nil)) => ResponseString(p)
}
In this case a plan is constructed directly from an anonymous partial function—that function is the intent. We can define the same plan in more explicit parts, as is usually necessary in a larger application.
sourceobject Echo extends unfiltered.filter.Plan {
def intent = {
case Path(Seg(p :: Nil)) => ResponseString(p)
}
}
Since this kind of plan is an implementation of the servlet filter interface, we can pass it directly to a servlet container.
sourceunfiltered.jetty.Server.anylocal.plan(Echo).run()
Passing on That
Since an intent is a partial function, it may be undefined for a request. In this case the request may be handled by some other intent or it could produce a 404 error from the server.
Unfiltered also supports an explicit mechanism to specify that a matched request should not be handled by an intent: the Pass
object.
sourceobject Public extends unfiltered.filter.Plan {
def intent = {
case Path(Seg("admin" :: _)) => Pass
case Path(Seg(path :: Nil)) => myRenderer(path)
}
}
This intent avoids handling anything under /admin
by matching that condition and passing on it. There are other ways to achieve the same ends, but an explicit Pass
is often the most straightforward.
Keep in mind that Scala’s partial functions are unaware of Unfiltered’s Pass
mechanism, so the intent above is in fact defined for the excluded paths. But when attached to a plan, requests that evaluate to Pass
are treated the same as those that are undefined.
Chaining Intents
When you want to combine intents within a single plan, use onPass
:
sourceobject MyPlan extends unfiltered.filter.Plan {
def intent = publicIntent.onPass(privateIntent)
val publicIntent = unfiltered.filter.Intent { ??? }
val privateIntent = unfiltered.filter.Intent { ??? }
}
The onPass
method is defined implicitly for PartialFunction
with the import of the unfiltered.response._
package. It works like orElse
but is aware of the Pass
object and it’s optimized to avoid unnecessary reevaluation of pattern guards.