8 Replies Latest reply on Aug 8, 2007 2:40 AM by jdyerjdyer

    Using a custom 404 page to perform a query

    silstorm
      I've spent a little while looking over to google results related to this, but haven't come across anything as yet that matches what I'm trying to do.

      The goal basically is to use 'search engine safe' urls, specifically I want to display www.mydomain.com/test but actually execute www.mydomain.com/search.cfm?s=test. From what I have gathered, the most effective way is to use a custom 404 page and a cfinclude of the search page within it.

      Now, the part I'm struggling with is how I grab the 'test' from 'www.mydomain.com/test' and set is as a variable I can pass in the cfinclude tag.

      The somewhat crude code I have taken a 'guess' with so far is as follows;

      <cfset cleanpath=REReplace(CGI.QUERY_STRING, "\?|\&|\=", "/" ,"ALL")>
      <cfset cleanpath=Replace(cleanpath, "404; http://#CGI.SERVER_NAME#:#CGI.SERVER_PORT#", "","ALL")>
      <cfinclude template="search.cfm?=#cleanpath#">

      The above currently results in an 'Invalid Argument' message.

      Any pointers or examples available would be gratefully appreciated!
        • 1. Re: Using a custom 404 page to perform a query
          ssawka
          Ok, you're a little confused on your tags. <cfinclude> allows you to insert the text of another file into your template. This would work, but the variable needs to be set before the include. An easier way, though possibly slower, would be to use a <cflocation> tag. So, you could do the following:

          • 2. Using a custom 404 page to perform a query
            jdyerjdyer
            This command strips the http:// off the file name.
            <cfset nohttp = mid(cgi.query_string, find("//",cgi.query_string)+2, len(cgi.query_string)-find("//",cgi.query_string)-1)>

            Then search for / with find, like this.
            <cfset fname = LCase(mid(nohttp, find("/",nohttp)+1, len(nohttp)-find("/",nohttp)))>

            Then strip the ending / if present.
            <cfif mid(fname,len(fname),1) eq "/"> <!--- Is there and ending /, if so, strip it. --->
            <cfset fname = mid(fname, 1, len(fname)-1)>
            </cfif>

            This leaves you with the relative path and the file name requested.
            If you just want the file name, you could split it on / and take the last item like this, but be sure to strip the / off the end first like above, otherwise this won't work, I don't think.
            <cfset fname = ListGetAt(fname,ListLen(fname,"/"),"/")>

            Hope this helps.
            • 4. Using a custom 404 page to perform a query
              silstorm Level 1
              Thanks jdyerjdyer - that has been a big help!

              If I exclude the final cfinclude and just use a cfoutput for the fname, it displays exactly the variable I want to pass on. However, the using the cfinclude is returning an Invalid Argument. If however I change the cfinclude to include the full domain (i.e mydomain.com/search.cfm?s=#fname#) I get the following error;

              The filename, directory name, or volume label syntax is incorrect

              That leads me to suspect everything is fine up until that point, but the cfinclude isn't functioning correctly. If I just use ../search.cfm?s=#fname# I'm fairly certain the path should be correct, so I'm a little stumped.
              • 5. Using a custom 404 page to perform a query
                jdyerjdyer Level 1
                Once you have the path and file, use ExpandPath on it to get the absolute path for your include.
                Then use this code to create a relative path to the file you want to include.
                function getRelativeDir(newdir)
                {
                var curr = GetBaseTemplatePath();
                var rel = Right(newdir,Len(newdir)-3);
                var i = 1;
                for (i=1; i LT ListLen(curr,'\'); i=i+1)
                {
                rel = "..\#rel#";
                }
                return rel;
                }

                That should make the include work. The problem you are probably having with the include is the directories. Cold Fusion still thinks you are processing the file originally requested. So the working directory is where that file is, not where your custom error page is.

                For example, if your website has the following structure,

                http://www.yoursite.com/customerror.cfm
                http://www.yoursite.com/somefolder/includethis.cfm

                where www.yoursite.com points to C:\wwwroot\
                then you would call Expand Path with customerror.cfm, or just pass in C:\wwwroot\somefolder\includethis.cfm to the function above.

                So say you are having people go to www.yoursite.com/imaginary_directory/username
                Cold Fusion sees the working directory as imaginary_directory/username even though it is currently processing the customerror.cfm file from the root directory. Because includes only accept relative paths to files, not absolute, you might have no idea what directory is being passed to customerror.cfm

                Your customer might have added a directory, or left off the imaginary_directory part, so if you were to try and hard code the include such as ../../somefolder/includethis.cfm it might fail. So you use the get relative directory to reverse traverse back to the root directory from the current working and forward to the file you want.

                I'm not sure if this is a feature, but I think it is because this allows you to not clutter up your website with folders or files with the same content. I use it to handle agent accounts. Each agent is given a unique folder name. When the website tries to access the non existant folder, it processes the customerr.cfm file and in that file I check the database to see if the directory passed in is a valid name, and has a database record. If so, I load that agents information into the application to display the website as though it was their own website. No having to make copies for everyone, or creating a bunch of agent folders. Nice and neat.

                Only one caveat, and that is actual errors. If you don't recognize the name passed in, then you now have to inform the user that it doesn't exist. I display a standard 404 message and let them know they will be redirected to the homepage in a few seconds.
                Then I use javascript window.location to move them with a setTimeOut function to handle the delay.
                • 6. Re: Using a custom 404 page to perform a query
                  silstorm Level 1
                  Thanks for your very detailed reply.

                  I'm not so sure the path is the problem - I tried a few other tests last night and found the follows;

                  - a cflocation on the same filename works without a problem
                  - the 404 document (in my case error.cfm) is located in the same directory as the search.cfm and always will be (except for 'genuine' 404s, but I'll address that later).

                  My only thought is that something in the code of the search.cfm page is causing the invalid argument error, but I really can't see what that could be. Obviously I can't be sure that is the problem either, but I can't understand why cfinclude would result in an invalid argument error and cflocation doesn't.
                  • 7. Re: Using a custom 404 page to perform a query
                    silstorm Level 1
                    Finally managed to figure it out!

                    Just had a bit of a Eureka moment and remembered that I can't pass variables with cfinclude, so I added a quick cfset for the URL.s variable before the cfinclude and now everything works perfectly!

                    Thanks so much for your help, it's been very much appreciated!
                    • 8. Re: Using a custom 404 page to perform a query
                      jdyerjdyer Level 1
                      The invalid argument has to do with the way you are including the file.
                      Included files expect a cfm file name only. No url arguments. This is a file name.
                      If you want to do this as an include, you would just set the variable name you want the included file to have access to and reference it as a variable in the file.

                      For instance, say you have the fname variable in the main file.
                      In the include file, use this
                      if (fname EQ "whatever")
                      {
                      //Do something
                      }

                      or

                      <cfif fname EQ "whatever">
                      <!--- Do something. --->
                      </cfif>