Reputation: 9190
I was thinking of skipping Phoenix since I am just planning to build a demo for a React app that uses some API routes for its state. Seems like an opportunity to familiarize with the underlying tech.
I came up with the following, but it feels very "hardcoded" and I am curious if there is a more elegant solution to achieve the same thing.
defmodule DemoApp.Plug.ServeStatic do
use Plug.Builder
@static_opts [at: "/", from: "priv/static"]
plug :default_index
plug Plug.Static, @static_opts
plug :default_404
plug Plug.Static, Keyword.put(@static_opts, :only, ["error_404.html"])
# Rewrite / to "index.html" so Plug.Static finds a match
def default_index(%{request_path: "/"} = conn, _opts) do
%{conn | :path_info => ["index.html"]}
end
def default_index(conn, _), do: conn
# Rewrite everything that wasn't found to an existing error file
def default_404(conn, _opts) do
%{conn | :path_info => ["error_404.html"]}
end
end
The idea is to have /
serve index.html
without redirect, and serve the contents of an error file whenever something isn't found, instead of a minimal response "404 file not found" string.
Is there a way to achieve this without plugging Plug.Static twice, or is this the way to go? I can also see the catchall :default_404
collide with my API routes later on, and I am not sure how I would resolve that.
Any input would be much appreciated. Thank you!
Upvotes: 2
Views: 1140
Reputation: 222428
I would use Plug.Router
and Plug.Conn.send_file/5
for this. Here's some code that does what you do but much cleaner:
defmodule M do
use Plug.Router
plug Plug.Static, at: "/", from: "priv/static"
plug :match
plug :dispatch
get "/" do
send_file(conn, 200, "priv/static/index.html")
end
match _ do
send_file(conn, 404, "priv/static/404.html")
end
end
As the :match
and :dispatch
are plugged in after Plug.Static
, any files in priv/static
will be served before falling back to the router, just like Phoenix does.
With these files in priv/static
:
➜ cat priv/static/404.html
404.html
➜ cat priv/static/index.html
index.html
➜ cat priv/static/other.html
other.html
Here's how this code works:
➜ curl http://localhost:4000
index.html
➜ curl http://localhost:4000/
index.html
➜ curl http://localhost:4000/index.html
index.html
➜ curl http://localhost:4000/other.html
other.html
➜ curl http://localhost:4000/foo
404.html
➜ curl http://localhost:4000/foo/bar
404.html
➜ curl http://localhost:4000/404.html
404.html
➜ curl -s -I http://localhost:4000/foo | grep HTTP
HTTP/1.1 404 Not Found
➜ curl -s -I http://localhost:4000/foo/bar | grep HTTP
HTTP/1.1 404 Not Found
➜ curl -s -I http://localhost:4000/404.html | grep HTTP
HTTP/1.1 200 OK
Upvotes: 7