How to troubleshoot 404 with LiftRules.statelessRewrite

52 views
Skip to first unread message

Christopher Oman

unread,
Oct 25, 2014, 2:15:44 AM10/25/14
to lif...@googlegroups.com
Hello. I am having trouble figuring out how to troubleshoot a problem I am having with the statelessRewrite.

I am following the cookbook recipe for avoiding CSS and JavaScript caching (http://chimera.labs.oreilly.com/books/1234000000030/ch02.html#AvoidAssetCaching). My code looks like this:

    val version = <generate version here>


   
LiftRules.attachResourceId = (path: String) => {
     
if(path.startsWith("/")) {
       
"/webcache/" + version + path
     
} else {
       
"/webcache/" + version + "/" + path
     
}
   
}


   
// Remove the cache/{resourceId} from the request if there is one
   
LiftRules.statelessRewrite.prepend(NamedPF("BrowserCacheAssist") {
       
case req @ RewriteRequest(ParsePath("webcache" :: id :: path, suffix, _, _), _, _) => {
         
RewriteResponse((path, suffix) :: Nil)

       
}

   
})


It took me about an hour of head-scratching to get the RewriteRequest to match. Apparently parts of the path that start with a number don't become part of the ParsePath, 

Anyway, I am still getting 404 on files that exist.

For example, if I take the data-lift="with-resource-id" off of a script tag, it loads just fine. But if I add in the "with-resrouce-id", it's not finding the file. 

I tried assigning the RewriteResponse to a val and printing it out and the ParsePath looks correct. For example, if I have a path of /static/css/app.css, the RewriteResponse looks like 

RewriteResponse(ParsePath(List(static, css, app),css,true,false),Map(),false)


I don't really know where to go from here. Some insight would be greatly appreciated.

Thanks.

Christopher Oman

unread,
Oct 25, 2014, 2:56:21 AM10/25/14
to lif...@googlegroups.com
I forgot to mention that I am using 3.0-M1

Diego Medina

unread,
Oct 25, 2014, 9:17:05 AM10/25/14
to Lift
Hi,

The cookbook has diff code than what you posted, have you tried using:

  LiftRules.statelessRewrite.prepend(NamedPF("BrowserCacheAssist") {
    case req @ RewriteRequest(ParsePath("webcache" :: id :: path, suffix, _, _), _, _) => {
      RewriteResponse(path , suffix ) //here is the diff
    }
  })


?

the code you posted doesn't compile in Lift  2.5.1 (I don't have a sample 3.0 project to test right now)





--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code

---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Diego Medina
Lift/Scala consultant
di...@fmpwizard.com
http://fmpwizard.telegr.am

Richard Dallaway

unread,
Oct 25, 2014, 10:10:11 AM10/25/14
to liftweb
In terms of debugging these things, what I tend to do is two things:
- look at the URL generated in the HTML or address bar to double check the input is really what I think it should be
- add logging in each case on the rewrite to see where I'm matching (included, temporarily, a catch all case to see where I'm not matching)

In case it helps, there's a further discussion on this area over at SO: http://stackoverflow.com/questions/17575161/lift-cookbook-avoiding-css-and-javascript-caching

Richard

Christopher Oman

unread,
Oct 25, 2014, 10:40:28 AM10/25/14
to lif...@googlegroups.com
My mistake. Somehow I managed to get an extra '(' in the code i pasted in. The code I have in my project only has 1 (.

Christopher Oman

unread,
Oct 25, 2014, 10:51:51 AM10/25/14
to lif...@googlegroups.com
I really should do a better job of self-editing for hitting reply. So please ignore my last reply.

The code I pasted in was edited because I had tried so many different things, I was kind of in the middle of some other things and got the edited code messed up.

So the code as it is in the project right now is:

 val version = <generate version here>


   
LiftRules.attachResourceId = (path: String) => {
     
if(path.startsWith("/")) {
       
"/webcache/" + version + path
     
} else {
       
"/webcache/" + version + "/" + path
     
}
   
}


   
// Remove the cache/{resourceId} from the request if there is one
   
LiftRules.statelessRewrite.prepend(NamedPF("BrowserCacheAssist") {
       
case req @ RewriteRequest(ParsePath("webcache" :: id :: path, suffix, _, _), _, _) => {

         
RewriteResponse(path, suffix)

       
}

   
})



So, yes fmpwizard, the code in my project as it stands right now does match what you suggest.

Richard,
In the case above, I did assign the RewriteResponse to a val, printed out the val, and then 'returned' that val. The output of that is what I had shown in my original response, the correct ParsePath from what I can tell.

I'll take a look at the SO discussion and see what I can glean from there.

At this point, I am still getting 404.

Diego Medina

unread,
Oct 25, 2014, 1:43:11 PM10/25/14
to Lift

Silly question but, if you go to the URL with no resource I'd, does the container serve the file?

Diego
Sent from my cell

--

Christopher Oman

unread,
Oct 25, 2014, 4:31:49 PM10/25/14
to lif...@googlegroups.com
Yes, it does.

Diego Medina

unread,
Oct 25, 2014, 5:59:24 PM10/25/14
to Lift
I guess at this point try to put together a sample app following these steps


so we can take a look and come up with a solution (and then we can tell you how we got it to work:) )

Thanks

Diego


Christopher Oman

unread,
Oct 25, 2014, 8:17:53 PM10/25/14
to lif...@googlegroups.com
Ok, I created a project from lift_blank. It is using the 3.0-M1 version.


Start the container up in sbt. In the browser, go to

- http://localhost:8080/static/css/app.css and notice the file is downloaded

Now go to 


Changes I made to the lift_blank template:
In build.sbt, chagned liftVersion to "3.0-M1" and the lift-jquery-module to 3.0
In the static folder, added the css folder and the app.css file in it
In Boot.scala, added the lines starting with val version and following.

Thanks for taking a look at this.

Chris 

Christopher Oman

unread,
Oct 25, 2014, 9:25:27 PM10/25/14
to lif...@googlegroups.com
I just to looking at the SO post. Huh, that seems somewhat convoluted. I will give that a try though.

Diego Medina

unread,
Oct 25, 2014, 11:10:32 PM10/25/14
to Lift
Thanks for the sample project, after trying a few different things, I think the problem here is that statelessRewrite only works for files/paths that Lift serves, css files, as well as images and a few other file types, are handled by the underlying container (jetty/tomcat/etc).

so, what statelessRewrite does is modify the Req url path early in the process of the request, but when Lift already took over serving the file.

as a simple test I went ahead and changed the extension of your css file from css to html, and then when I visit:


I get the content of the css file, but the type reported by the Response Headers in Chrome is text/html.

So, a few things:

1- Looks like the cookbook is wrong and we should remove that section about doing a rewrite of the path, unless we also need to set some other setting to make it work.
2- You could read the content of the css file and server them from inside Lift, I wouldn't personally do this
3- The way I handle this original use case is by using something like  grunt http://gruntjs.com/ to append a random string to the actuak name of the css /js file, this way if I change the css file, grunt generates a new string and the filename is diff.

Tim Nelson's example project here https://github.com/eltimn/lift-extras/tree/master/example has a sample Lift project using grunt

I would go with Grunt (or similar), I have been using it for over a year, it works really well and provides some other useful features. This is a short post I wrote a while ago related to it http://blog.fmpwizard.com/blog/using-grunt-and-bowerwith-lift

And about debugging this, just like you did, plenty of println until you get what you are looking for, but in this case there was some framework inner  knowledge that you also needed.

Hope that helps.

Diego

Diego




Richard Dallaway

unread,
Oct 26, 2014, 9:14:35 AM10/26/14
to liftweb

I wonder if the way to do this is with a servlet filter before the request reaches Lift. I don't know how to do that, or if it's possible, but I might have a dig around that area.

Sent from a mobile device, so may be terse.

Antonio Salazar Cardozo

unread,
Oct 27, 2014, 10:01:59 AM10/27/14
to lif...@googlegroups.com
Hmm… So are you dealing with a proxy that can't deal with query parameters? Otherwise
there's no reason to do the path rewriting. If you are dealing with such a proxy, yeah, you
can either use a servlet filter, or you can use nginx to do the rewriting for you.
Thanks,
Antonio
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Diego Medina
Lift/Scala consultant
di...@fmpwizard.com
http://fmpwizard.telegr.am

--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code

---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+unsubscribe@googlegroups.com.

Christopher Oman

unread,
Oct 28, 2014, 11:51:24 AM10/28/14
to lif...@googlegroups.com
The lead UI developer on our team didn't want the query parameter as he has had trouble with IE caching the file even with the query parameter. Now, that may be an issue with older IE versions, but he still wanted it changed.

My solution doesn't really help any one else in this group, because as I found out later, we already have a mechanism in place to deal with versioning of files. Our application runs in a Jetty server. We have installed a request handler early in the Jetty pipeline that looks for the version and rewrites the request before going to the web application.

So, my method in Boot became

    LiftRules.attachResourceId = (path: String) => {
     
if (path.startsWith("/")) {

        version
+ path
     
} else {

        version
+ "/" + path
     
}
   
}


Thanks for all of the support.

Antonio Salazar Cardozo

unread,
Oct 28, 2014, 1:24:59 PM10/28/14
to lif...@googlegroups.com
Awesome. For reference, we use sbt-resource-management to do that for us. Hoping
at some point to have time to add at least basic built-in handling to Lift as an sbt plugin
or something.
Thanks,
Antonio

Richard Dallaway

unread,
Nov 15, 2014, 3:15:16 PM11/15/14
to liftweb
My plan with this is to remove the re-writing part from the recipe and simply say to re-write in the web layer or as a servlet filter.

I have noddled on some servlet code for this part:

https://gist.github.com/d6y/53b060e0e3305536ec9f

...but it strikes me that this relies on knowledge of Lift internals (access to getRequiestURI from the servlet API).  And maybe that's a bad thing to suggest anyone does.

Richard

Antonio Salazar Cardozo

unread,
Nov 15, 2014, 4:00:28 PM11/15/14
to lif...@googlegroups.com
I think if it were to be done as Servlet filter, the right thing to do would be to make sure any
part of an HTTPServletRequest that is expected to return the URI with the cache string is
returning it without. As long as that's true, it doesn't matter what Lift internally does. So if
there are any other methods that allow accessing the request path, making sure that the
cache path isn't there either is reasonable for that filter. 
Thanks,
Antonio
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Diego Medina
Lift/Scala consultant
di...@fmpwizard.com
http://fmpwizard.telegr.am

--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code

---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages