Fork me on GitHub
#ring
<
2017-03-27
>
petr.mensik00:03:39

Hello, I am wondering how to correctly serve static files in a uberwar. I have bunch of static files (html, css, etc) under resources/public folder. I serve the login page with following route (GET "/" [] (resource-response "login.html")). This works fine because I defined public folder as resource-path in project.clj. However links between the files are broken (for example login page won't find its CSS file which is linked with href="styles/login-styles.css"). So what is the best way how to overcome this? I also tried defining resources as :war-resource-paths but then I cannot even serve the login page itself. Thanks for any advice 🙂

weavejester10:03:48

@petr.mensik are you using the wrap-resource middleware?

weavejester10:03:41

Also you don’t want to set resources/public as a resources-path

weavejester10:03:52

There’s a reason we use a subdirectory.

petr.mensik12:03:35

@weavejester I am using compojure-api so I am not sure whether it's included in default middleware

petr.mensik12:03:43

let me take a look

petr.mensik12:03:13

so the subdir for frontend part of the application should be something like resources/public/my-app?

petr.mensik12:03:31

ok, looks like it's not so I'll try adding it

petr.mensik12:03:32

ok, defining middleware as :middleware [wrap-session wrap-not-modified [wrap-resource "public"]]} worked for me (EDIT - worked with lein ring server, not in uberwar). However I am still wondering what is the best practice for static content serving? You mentioned that using resources/public is wrong, can I ask what would be a better solution? thanks a lot

weavejester14:03:21

@petr.mensik Using resources/public is correct; it’s adding resources/public to the classpath that’s wrong. You need some way of distinguishing between resources you want to make public, and resources you want to keep private.

weavejester14:03:57

https://github.com/ring-clojure/ring-defaults is a library that sets up sensible default middleware, and includes wrap-resource.

petr.mensik14:03:40

To me it's still makes sense to put all the website related stuff (html, css, ...) into resources/public and make it available to the classpath. Then I can have let's say resources/images and these will be for internal use of my application and I am not going to make that public. So is anything wrong such a setup?

petr.mensik14:03:53

So it sounds to me I should have :war-resource-paths ["resources/public"] and :resource-paths ["resources/images"]

petr.mensik14:03:39

With the only problem that this doesn't work when I package the application into the WAR file with lein ring uberwar. Again, links between files are broken (and I guess this is because Tomcat is not prepending the application name before link)

petr.mensik14:03:20

Because servers the login page without styles, however works. So is there any standard way how to make application work for both development and deployment? Because I feel like I am trying to reinvent wheel right now 😕

petr.mensik14:03:48

And I cannot be the first one who wants to have relative links working when serving static content 🙂

weavejester15:03:26

@petr.mensik No, you shouldn’t do that. Let’s say you have a file resources/public/foo.html. If you set your :resource-paths to [”resources”], which is the default, then you’ll be able to access your HTML file at public/foo.html.

weavejester15:03:29

We can then say “any resource that starts with public/ is accessible”

weavejester15:03:24

But if you set the :resource-paths to [”resources/public”], then your HTML file will be accessible at foo.html, and how do you know if that is public or not?

weavejester15:03:29

Also, ignore :war-resource-paths. Those are for war resources, which are different to the normal resources. They’re not on the classpath. Typically we don’t use them in Clojure, we just use normal Java resources.

petr.mensik15:03:04

Ok, that makes sense, thanks. However I am not sure how for instance login.htmlcan link styles.css when the CSS file is burried inside the WAR file under WEB-INF/classes/public

weavejester15:03:45

It doesn’t matter where it is in the jar, or where it is on the filesystem. What matters is where the classpath is.

weavejester15:03:01

So WEB-INF/classes is part of the classpath.

weavejester15:03:38

Effectively the classpath is lots of different places for resources merged together

weavejester15:03:52

Java tries them in order until it finds a matching file

weavejester15:03:19

While you’re developing, src, test and resources are all on your classpath.

weavejester15:03:02

So if you try to find the resource public/foo.html, it’ll look for a file src/public/foo.html, ‘test/public/foo.html` and resources/public/foo.html.

petr.mensik15:03:26

sure, I got this one. However what I meant is that if one static file is referencing other (like the example above), then it breaks. At least in my case, I can correctly serve login.html with (GET "/" [] (resource-response "login.html" {:root "public"})) however login.html doesn't see the main.css specified as href="styles/main.css"

weavejester15:03:28

Do you have a resources/public/styles/main.css file?

weavejester15:03:26

Also if you’re serving from a subpath, styles/main.css might be problematic

petr.mensik15:03:04

2) what do you mean?

petr.mensik15:03:43

Sorry for being noob here, I just have a feeling that I am missing something important so I'd rather ask than stay as dumb as I am now 🙂

weavejester15:03:16

With relative paths, the ending slash on a URL is significant

weavejester15:03:19

So for instance...

weavejester15:03:59

If your page is at /foo, then a link to styles/main.css will go to /styles/main.css

weavejester15:03:21

If your page is at /foo/, then a link to styles/main.css will go to /foo/styles/main.css.

weavejester16:03:21

So if your war file is being loaded from a subpath, you might run into problems with relative paths.

weavejester16:03:19

That’s why Hiccup has the wrap-base-url middleware, for instance

petr.mensik16:03:02

That's the thing, I am refactoring older application so I dont really want to modify whole frontend part.

weavejester16:03:54

Are you serving from a subpath?

weavejester16:03:33

And does the issue of not being able to find styles/main.css only occur when you’ve deployed via a war?

alex.ter.weele16:03:32

Is it possible to use compojure middleware to reject a request with a Bad Request status?

weavejester16:03:33

Just return a 400 status code if the request is bad.

alex.ter.weele21:03:07

ah, makes sense. I think I can make it work but I’m struggling with spec now 😛

alex.ter.weele23:03:11

My application isn’t returning ’() when I expect it to. I think it’s because (isa? '() compojure.response/Renderable) is false. Renderable is extended to ISeq, but (isa? '() clojure.lang.ISeq) is also false. Is this by design? I can file a bug report if desired.