Unable to get !-d and !-f to work correctly

Topics: Developer Forum, User Forum
Jul 15, 2008 at 2:19 PM
Hi, I'm hoping someone can help me with my rules and conditions.  I'm so close to getting it working but everytime I solve one problem, another one opens.

In a nutshell, what I'm trying to do is this:
* Test to see if the file being requested exists - if it does, do nothing
* Next, do not do a rewrite if a requested resource has one of a number of different extensions or is in a particular path
* Finally, rewrite any other URL --> index.asp and retain the querystring if there is one.

All the rewriting works apart from the conditions to do no rewriting if the file or directory exists.  Here's the content of my ini file:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^(.*\.js)$ $1 [I,L]
RewriteRule ^(.*\.gif)$ $1 [I,L]
RewriteRule ^(.*\.png)$ $1 [I,L]
RewriteRule ^(.*\.jpg)$ $1 [I,L]
RewriteRule ^(.*\.jpeg)$ $1 [I,L]
RewriteRule ^(.*\.swf)$ $1 [I,L]
RewriteRule ^(.*\.flv)$ $1 [I,L]

RewriteRule ^/images/(.*)$ /images/$1 [I,L]
RewriteRule ^/css/(.*)$ /css/$1 [I,L]

RewriteRule ^.*\?(.*)$ /index.asp?$1 [U,I,L]
RewriteRule ^.*$ /index.asp [U,I,L]

Can anyone help me with this as it's driving me mad!  Also, can my final two rewrite rules be combined into one?  It looks as if it should be possible but I haven't been able to work out how.

Thanks for any help,

Anthony.

Coordinator
Jul 15, 2008 at 3:49 PM
Edited Jul 15, 2008 at 3:52 PM

Anthony!

First, you should know that the RewriteCond statements you have, apply only to the successive RewriteRule.  So they apply only to the first one, as you have your rules written.   The first rule is the one dealing with .js extensions.  I think that is not what you want, from the plain-english description of what you're trying to do.

It is possible to collapse some of your rules by using compound regular expressions.  Instead of this:

RewriteRule ^(.*\.js)$ $1 [I,L]
RewriteRule ^(.*\.gif)$ $1 [I,L]
RewriteRule ^(.*\.png)$ $1 [I,L]
RewriteRule ^(.*\.jpg)$ $1 [I,L]
RewriteRule ^(.*\.jpeg)$ $1 [I,L]
RewriteRule ^(.*\.swf)$ $1 [I,L]
RewriteRule ^(.*\.flv)$ $1 [I,L]

this:

RewriteRule ^(.*\.(js|gif|png|jpg|jpeg|swf|flv))$ $1 [I,L]

And if you do that, then the RewriteCond statements you have apply to that single collapsed rule.  The resulting rule says "if it's an image or swf or js file (etc) then do nothing.   If you have the conditions on that rule, then the rule applies only if the filename does not exist as either a file or a directory.  I'm not sure if that's what you want or not.

As for combining the final two rules into one - yes. you can do that.

Jul 15, 2008 at 3:58 PM
Edited Jul 15, 2008 at 4:02 PM
Thanks for your message - I did suspect that perhaps those conditions only applied to only the very next rewriterule and indeed I did have them lower in my list before. 

In terms of combining those two last rules, I need any request (except for those I've excluded above) to rewrite to a specific URL.  I've split the rule into two at the moment as I wasn't able to retain the querystring correctly with just one rule.  So what I need is:

/ to rewrite to /index.asp
/blah to rewrite to /index.asp
/blah/blah to rewrite to /index.asp
/?a=1&b=2 to rewrite to /index.asp?a=1&b=2
/blah?a=1&b=2 to rewrite to /index.asp?a=1&b=2
/blah/blah/?a=1&b=2 to rewrite to /index.asp?a=1&b=2
etc.

My code then uses the Request.ServerVariable("HTTP_X_REWRITE_URL") to work out what page to serve.

Hope that makes sense and thanks for your help!
Jul 15, 2008 at 6:12 PM
Edited Jul 15, 2008 at 6:19 PM
Edit:
Well I thought I worked it out but now the querystring is disappearing again.  I'm sure it was working a minute ago...!
---

Looks like I've worked it out.  Here's my latest set of rules:

RewriteRule ^(.*\.(js|gif|png|jpg|jpeg|swf|flv))$ $1 [I,L]
RewriteRule ^/(images|css)/(.*)$ /$1/$2 [I,L]
RewriteCond %{REQUEST_FILENAME}	!-f
RewriteRule ^.*\??(.*)$	/index.asp?$1 [U,I,L]

I've had to remove:

RewriteCond %{REQUEST_FILENAME} !-d
in order for the root of the site to be rewritten.  Hardly the end of the world if other sub-directories that actually exist are rewritten - I can always add them to the rules above to prevent this happening.

One nice addition that might be worth making to IIRF is a way of telling IIRF to do nothing if a pattern matches in a rewrite rule.  So instead of having to use something like this to force a URL to be the same:
RewriteRule ^/(images|css)/(.*)$ /$1/$2 [I,L]

you'd instead use:
RewriteRule ^/(images|css)/(.*)$ - [I,L]
Qwerksoft's IIS Rewrite uses this method and I think it seems quite nice (or am I missing a similarly easy method in IIRF?)

Cheers.

Coordinator
Jul 15, 2008 at 6:44 PM
Edited Jul 15, 2008 at 6:47 PM

Glad you are getting there.
I like your suggestion about the "do nothing" rule.

I tried your final rule in the testdriver, and it did not work for me the way you said you wanted it to work.  If you want a regex that captures any querystring that is passed in, here is something that worked for me:

# This regex says: 
# Replace any URL with index.asp, followed by any query string that was in the original request.
# The "any query string" is captured by $1, and it is either a string beginning with a ?
# (represented by (\?.*) in the regex) or it is the empty string (represented by () in the regex).

RewriteRule ^[^?]+((\?.*)|())$ /index.asp$1 [U,I,L]
Jul 15, 2008 at 9:35 PM
Thanks Cheeso, that worked a treat!
Coordinator
Jul 16, 2008 at 4:19 PM
Cheers.
And remember, IIRF is DonationWare. 
http://cheeso.members.winisp.net/IirfDonate.aspx