com-lihaoyi / cask

Cask: a Scala HTTP micro-framework. Cask makes it easy to set up a website, backend server, or REST API using Scala
https://com-lihaoyi.github.io/cask/
Other
525 stars 55 forks source link

Support for URL rewriting? #94

Closed cpitclaudel closed 8 months ago

cpitclaudel commented 9 months ago

Hi,

I'm trying to use case for a very simple website: a few web pages wrapping a simple REST API. My routes look like this:

  @cask.getJson("/api/")
  def api(exprType: ExprType, expr: String) = ???

  @cask.staticFiles("/www/")
  def sourcesStaticFileRoute() = STATIC_PATH

  @cask.get("/")
  def indexRedirect() = cask.Redirect("/www/index.html")

There are two problems with this approach:

  1. Users see the redirect to /www/index.html.
  2. Static files are served under /www instead of /

In Apache or nginx I would use a URL rewrite instead to rewrite / to /www/ transparently except when the path starts with /api. I have not found a way to do this in Cask. Hence, I have three questions:

  1. Is there a way to do a recursive resolution on the server side for a single file, so that I could, in the body of the / route, call the staticFiles route to get the contents of /www/index.html?
  2. More generally, is there a way to do URL rewriting for a whole URL prefix, so that I could redirect all / queries to /www, except /api?
  3. Is there a way to have a staticFiles route that overlaps with other routes, to be used as a fallback when other routes don't match?

The main blocker for me is the index.html rewrite; I can live with the non-overlapping routes.

Thanks a lot!

lihaoyi commented 8 months ago

We don't support rewrite rules, and I'm not sure we want to. This kind of rewrite rule can really get very confusing, and it really goes against Cask's routing philosophy of having a single tree of endpoints that can be traversed in one pass (v.s. the "try every route in sequence and find one that matches" approach that other frameworks do.

IMO if you want rewrite rules, easiest thing is to just throw nginx in front of your Cask web server. That's what I do in a bunch of places for a variety of reasons, and it works well enough. nginx is a pretty small process and can easily fit inside the same docker container or deployment

For index.html, I would do

 @cask.get("/")
  def index() = cask.Response(
    os.read(os.Path(STATIC_PATH) / "index.html"),
    200,
    headers = Seq("Content-Type" -> "text/html; charset=utf-8")
  )