The behaviour a host application implements to say which paths it owns.
A resolver is asked about a path only when no declared route matched it —
which is to say, only about requests that would otherwise be a 404. It
answers :pass to let the 404 happen, or {:match, term} to claim the
request, and whatever it returns comes back to the controller through
DynamicRoutes.resolution/1.
defmodule MyApp.Pages do
@behaviour DynamicRoutes.Resolver
@impl true
def resolve(["blog", slug]) do
case MyApp.Content.page_by_slug(slug) do
nil -> :pass
page -> {:match, page}
end
end
def resolve([slug]) do
case MyApp.Content.page_by_slug(slug) do
nil -> :pass
page -> {:match, page}
end
end
def resolve(_path), do: :pass
endReturning the record itself, rather than just true, is worth doing: the
controller would otherwise look it up a second time on the same request.
Caching
Results are cached, so resolve/1 is not called on every request for the
same path — see DynamicRoutes.Cache. Call DynamicRoutes.invalidate/0
when pages change, or DynamicRoutes.invalidate/1 for a single path.
Because the cache holds whatever {:match, term} carries, a resolver that
returns a large struct is holding that struct in memory until the entry
expires. Return an id, or something small, if the record is big.
Summary
Types
The answer to "is this path yours?"
Callbacks
Decides whether a path is one this application serves dynamically.
Types
@type resolution() :: :pass | {:match, term()}
The answer to "is this path yours?"
{:match, term} claims the request and hands term to the controller.
:pass declines, and the request 404s as it normally would.
Callbacks
@callback resolve(path_info :: [String.t()]) :: resolution()
Decides whether a path is one this application serves dynamically.
Receives the request path split on /, with no leading empty segment and
already percent-decoded — so a request for /caf%C3%A9 arrives as
["café"], matching what the controller will see in its params.
Do not decode again. %2520 is a literal %20, and decoding it twice turns
it into a space the client never asked for.
This runs on requests that matched no declared route, which on a public site
includes every scanner probing for /wp-login.php. Keep it cheap, and
return :pass early for shapes that cannot be yours.