I love how one can define a host where the last bit would automatically get matched:
scope "/", host: "about.mysite." do`
But it’s impossible to do the opposite end:
scope "/", host: ".mysite.com" do
(for matching all remaining subdomains)
What are the the implications that prevent this from being possible and what are the alternatives for implementing wildcard subdomain matching in a Phoenix project?
Compiling 1 file (.ex)
error: a binary field without size is only allowed at the end of a binary pattern, at the right side of binary concatenation and never allowed in binary generators. The following examples are invalid:
rest <> "foo"
<<rest::binary, "foo">>
"foo" <> rest
<<"foo", rest::binary>>
lib/my_app_web/router.ex:1
In short, the “match any subdomain” clause in the router is to just not specify the :host option. The subdomain itself will be available in conn.host so you could pull it out in a plug and do whatever you need to do with it.
If you want different routes depending on the presence of a subdomain, that’s a little different. There are a few discussions around here about it but I’m having trouble finding the ones I found helpful. I do have the (abandon) project I was using it in, though. In short you want to make a plug, check if there is a subdomain then delegate to another router. Maybe there is an easier way at this point? In any event, here is my code:
# lib/my_app_web/subdomain_router.ex
defmodule MyAppWeb.SubdomainRouter do
# This is just a regular router but with subdomain specific routes
end
# lib/my_app_web/plugs/subdomain_plug
defmodule MyAppWeb.SubdomainPlug do
import Plug.Conn
def init(opts), do: opts
def call(conn, router) do
case get_subdomain(conn.host) do
subdomain when byte_size(subdomain) > 0 ->
conn
|> fetch_session()
|> put_session(:subdomain, subdomain)
|> router.call(router.init({}))
|> halt()
_ -> conn
end
end
defp get_subdomain(host) do
root_host = MyAppWeb.Endpoint.config(:url)[:host]
if root_host == "localhost" do
# This handles tests
""
else
String.replace(host, ~r/.?#{root_host}/, "")
end
end
end
# BOTTOM of lib/my_app_web/endpoint.ex
defmodule MyAppWeb.Endpoint do
# ...
plug MyAppWeb.SubdomainPlug, MyAppWeb.SubdomainRouter
plug MyAppWeb.Router
end
Maybe it’s easier at this point? I did always find this to be a little bit of a pain point in Phoenix. Actually, have you tried simply host: "." at all? I can’t remember if that’s a thing or not and don’t feel like spinning up that app