URL Rewrite with IIS using ColdFusion - coldfusion

I've done a bit of searching but just can't put it all together. Here's what I need:
I'd like someone to surf to:
www.mysite.com/thisPlace
and have it redirect them to
www.mysite.com/template.cfm?pm=ms&loc_id=4
To do so, I somehow need to capture that they didn't request an existing file in their http request and run a .cfm page that queried the database for a record where locationName = 'thisPlace' and then redirect them to a page like
template.cfm?pm=ms&loc_id=4, where 4 is the record id of the row that matched 'thisPlace'

If your default document in IIS is set to index.cfm you could create a folder (directory) called "thisPlace" and place an index.cfm file that contains nothing but a <cflocation> tag and the accompanying query/logic to figure the URL.
Website.com/thisPlace would then function as you describe.
Edit:
You could add a custom 404 page...
Make it a .cfm file instead of html. Scan the template path to see what the user is looking for. If you find it in your database, redirect them there, else redirect them to a general 404 page.
<!---Up to a certain point (the directory in which you store your code) this will always be the same so you can hard-code your number --->
<cfset QueryConstant = #LEFT(CGI.CF_Template_Path, 22)#>
<!---Find the overall length of the template path. --->
<cfset QueryVariable = #Len(CGI.CF_Template_Path)#>
<!---Take whatever is past your QueryConstant (AKA the string that produces a 404 error.) --->
<cfset theRightNumber = QueryVariable - 22>
<cfset QuerySearchString = #RIGHT(CGI.CF_Template_Path, theRightNumber)#>
<cfquery name="ListOfLocations" datasource="CRM">
SELECT TOP 1 LocationID
FROM LocationTable
WHERE LocationName LIKE '%#QuerySearchString#%'
</cfquery>
<cfif ListOfLocations.recordcount>
<cflocation url="/SomePage.cfm?LocationID=#ListOfLocations.LocationID#">
<cfelse>
<cflocation url="/Regular404page.html">
</cfif>

Thanks guys! Huge help! Using your inputs, here's what I did:
(had to use QUERY_STRING instead of CF_Template_Path, as CF_Template_Path did not pass along anything after the url of the custom error page.
I set up a custom 404 error Execute URL in IIS to a file named check404error.cfm.
When someone looks for www.example.com/thisPlace, IIS sends them to http://www.example.com/check404error.cfm. I use the CGI.QUERY_STRING (404;http://www.example.com:443/thisPlace) to ultimately get the "thisPlace" string to search with.
<!---Up to a certain point (the directory in which you store your code) this will always be the same so you can hard-code your number --->
<cfset QueryConstant = #LEFT(CGI.QUERY_STRING, 31)#>
<!---Find the overall length of the template path. --->
<!---31 is the length of '404;http://www.example.com:443/' --->
<cfset QueryVariable = #Len(CGI.QUERY_STRING)#>
<!---Take whatever is past your QueryConstant (AKA the string that produces a 404 error.) --->
<cfset theRightNumber = QueryVariable - 31>
<cfset QuerySearchString = #RIGHT(CGI.QUERY_STRING, theRightNumber)#>
<cfquery name="ListOfLocations" datasource="#request.dsn#">
SELECT location.id
FROM location WHERE url_name = <cfqueryparam value="#QuerySearchString#" cfsqltype="CF_SQL_VARCHAR" maxlength="255"> LIMIT 1
</cfquery>
<cfif ListOfLocations.recordcount>
<cflocation url="https://example.com/template.cfm?pm=ms&loc_id=#ListOfLocations.id#" addtoken="no" statusCode="301">
<cfelse>
<cflocation url="/404error.cfm" addtoken="no">
</cfif>

This is how most popular MVC Framework work today, by parsing out the URL segments.
Do you have access to any kind of URL rewrite software?
Since you are using IIS it has a built in rewrite engine where you could simply rewrite these kind of requests to a known file saving you the overhead of sending a 404 reply and parsing that out and having more request created as a result of that.
See http://wiki.coldbox.org/wiki/URLMappings.cfm for details. We use the Isapi rewrite version version to do just what you are asking for
Receive request for www.mysite.com/thisPlace
thisPlace isn't a directory
thisPlace isn't a file
Resend to index.cfm or a location of your chosing for additional parsing
Helicon Rewrite sends an HTTP header named HTTP_X_REWRITE_URL with the original requested URL so parsing it out is then very easy.
This all happens inline withing the one request so the client is never redirected.

Related

Coldfusion cflocation strange behavior

I am storing a google review URL in my database as:
https://www.google.com/search?CFID=ac59cfdf-bbad-4017-9759-e88054f3f242&CFTOKEN=0&q=njcomputerrepair%2Bbrick%2Bnj&oq=njcomp&aqs=chrome.1.69i60j69i59j69i60j69i57j0l2.2762j0j9&sourceid=chrome&ie=UTF-8#lrd=0x89c18348735c2907:0x59aa614832a36b22,3,
And then in my application I set that URL to a variable and I redirect the user to that URL using cflocation.
<cfquery name="geturl" datasource="#datasource#">
select (residential_ReviewURL) as redirectURL
from subscribers
</cfquery>
<!--- Redirect to main html redirect page --->
<cfoutput>
<cflocation url="#getURL.redirectURL#">
</cfoutput>
However the URL gets changed at some point because I think that Coldfusion doesn't like the characters in the URL and it replaces them with % or removes them. Therefore when the user hits the google page, the page doesn't process as it should.
Here is how the URL looks after the redirect:
https://www.google.com/search?CFID=ac59cfdf-bbad-4017-9759-e88054f3f242&CFTOKEN=0&CFID=ac59cfdf-bbad-4017-9759-e88054f3f242&CFTOKEN=0&q=njcomputerrepair%2Bbrick%2Bnj&oq=njcomp&aqs=chrome.1.69i60j69i59j69i60j69i57j0l2.2762j0j9&sourceid=chrome&ie=UTF-8#lrd%3D0x89c18348735c2907%3A0x59aa614832a36b22%2C3%2C
How can I stop ColdFusion from changing the URL and keep id exactly as how it is stored in the database?
UPDATE
So I found that URLdecode will preserve the string. Here is what I have.
#urlDecode(getURL.redirectURL)#
The output is as follows
https://www.google.com/search?CFID=ac59cfdf-bbad-4017-9759-e88054f3f242&CFTOKEN=0&q=njcomputerrepair+brick+nj&oq=njcomp&aqs=chrome.1.69i60j69i59j69i60j69i57j0l2.2762j0j9&sourceid=chrome&ie=UTF-8#lrd=0x89c18348735c2907:0x59aa614832a36b22,3,
Why is it adding CFID and CFTOKEN to the URL though? I have it turned off in my Application.CFM:
<cfapplication name="yaya"
clientmanagement="no"
sessionmanagement="no"
setclientcookies="no"
setdomaincookies="no"
sessiontimeout="#CreateTimeSpan(0,2,0,0)#"
applicationtimeout="#CreateTimeSpan(1,0,0,0)#"
>
To help others coming here:
cflocation have a parameter addToken which needs to set to no if we do not want to add CFID and CFTOKEN to the generated URL.
Adobe CFML reference: https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-tags/tags-j-l/cflocation.html

coldfusion 10 cfinclude gives site cannot be reached error

I have a action page that redirects to a page based on condition.
I thought of Including that page insted of redirection.
So i used cfinclude function to include this file.
But including that file doesn't work.
But when i open that page in browser it is working fine without error.
For Ex.
http://domain.com/page2.cfm?cutomerID=10
is working fine.
IN http://domain.com/page1.cfm
I am including below code.
<cfset url.customerID = 10>
<cfinclude template="page2.cfm">
Even i tried to add only HTML content which i get in page source for "http://domain.com/page2.cfm?cutomerID=10"
It is still not working including a coldfusion page with just html text without any coldfusion code.
Strange thing is When i tried to remove some text from this html content it works for some time but when i reload it stops and give the below error again.
I am not sure if cfinclude has to do anything page length or some other factors.
Screenshot for error is attached below.
Error Screenshot Image
If you are asking for a link you can have it as:
<cfoutput>
<cfset href="domain.com/page2.cfm?customerID=#customerid#&etc=#etc#" />
</cfoutput>
For your question you can use this:
<cfset custID = 10/>
<cfinclude template="page2.cfm">
In page2.cfm:
<cfif isDefined(url.customerID) || isDefined(custID)>
<!--- your etc code here --->
</cfif>
Note: If you don't want to use url.customerID you can simply delete the occurance

Dynamic Subdomains - ColdFusion 11

Anyone know a way to create dynamic subdomains in coldfusion 11 without having to add them to a dns server?
I want to be able to redirect each of my clients in a personalized subdomain every time they log in to my system.
ex: client1.example.com client2.example.com
This works if your site is hosted in a Windows IIS server.
If you are using IIS then the following can help you. Use loops and conditions as your requirements.
<!--- Provide you IIS SiteName --->
<cfset siteName = "Your IIS Site Name">
<!--- Your new domain address --->
<cfset newSiteBinding = "client2.example.com">
<!--- your port address --->
<cfset newSitePort = 80>
<cfset fileID = createUUID()>
<cfsavecontent variable="ex"><cfoutput>cd %windir%\system32\inetsrv
%windir%\system32\inetsrv\APPCMD set site /site.name: #siteName# /+bindings.[protocol='http',bindingInformation='*:#newSitePort#:#newSiteBinding#']</cfoutput>
</cfsavecontent>
<cffile action = "write"
file = "E:\#fileID#.bat"
output = "#ex#"
/>
<cfexecute name="E:\#fileID#.bat"
arguments="/c set"
variable="data"
timeout="10"
/>
<cffile action = "delete"
file = "E:\#fileID#.bat"
>
What we are basically doing is that we are creating a .bat file and executing it using cfexecute. Please note that you need to be careful with the paths because the Directory structure might be different in your server.
.BAT file content Example
cd %windir%\system32\inetsrv
%windir%\system32\inetsrv\APPCMD set site /site.name: example /+bindings.[protocol='http',bindingInformation='client2.example.com:80:*']
How the command works. Notice the + at /+bindings?
That means adding a new binding and /-bindings tries to remove an existing binding.

Pass queried parameters to ColdFusion file multiple times?

I have an cfmail function set-up in a particular file, email_output.cfm, which requires an ID passed to it to work properly, like email_output.cfm?ID=1. I want to set up a cron job that runs through a query returning the various needed IDs to pass. In testing, I can do the following:
<cflocation url="email_output.cfm?ID=10" >
But, since cflocation stops all other execution and opens another page, I can't loop through it. How would I pass parameters from a query to a single CF page multiple times?
Thanks - Joe
A custom tag sample implementation of this...
If this is your first time using a custom tag, it's easiest to put it in the same folder as the page calling it. There are a few options for putting it in a different directory, but let's start simple.
EmailMembers.cfm
<cfquery name="GetUIDs">
select userid from users
</cfquery>
<cfoutput query="GetUIDs">
<cf_maileach uid="#userID#">
</cfoutput>
Notice how I called my tag cf_maileach?
In the same directory, place maileach.cfm, see how the names match?
maileach.cfm
<cfif StructKeyExists(attributes,"uid") and val(attributes.uid) gt 0>
<cfquery name="getinfo">
select fname,lname,email
from users
where userID = <cfqueryparam cfsqltype="cf_sql_integer" value="#attributes.uid#">
</cfquery>
<cfmail to="#getinfo.email#" subject="Hi #getinfo.fname#">...</cfmail>
</cfif>
Notes
Depending on your version of cf, and whether you're using application.cfc or not, there are several ways to place a custom tag in an outside directory. There is also <cfmodule>
This is a sample only, something this basic is redundant, I was just trying to mimic what asker outlined. In this sample, I'm calling a query that could get all the data, only to use it to query row by row.
If you're not familiar with <cfqueryparam>, look it up, use it, love it.
Edit: While a CFHTTP method can serve this purpose, it suffers a few problems
Sessions are not automatically passed (even if the requesting server and destination server are the same.).
The page is accessed like a browser request. Application/OnRequestEnd are processed (and since session info is passed as well, this can cause problems trying to access files in secured areas.
Because of the above, the page would need to be in a folder with its own Application file to negate any application files above it in the directory hierarchy.
To combat 1, 2, and 3, You'd need to code in a layer of security, similar to your application's own security, so that the file is not vulnerable should the url be found.
Each call to the file via cfhttp would need to invoke some extra security checking.
It is significantly slower. In a very simple test with a zero-content application.cfc, the custom tag method was executing in literally <= 1/100th of the time. As actual function is added to the method, the difference in results would change.
Here is some sample code to test this yourself.
Contents of folder "safe":
Application.cfc
[ blank file, to negate my testing site's actual application.cfc ]
Testrun.cfm
<cfoutput><cfset starttick = GetTickCount()>
<cfloop from="1" to="20" index="i">
<cfhttp url="http://mysamesite.com/safe/http.cfm?u=#i#" method="get" result="test">
#test.filecontent#<br>
</cfloop>
CFHTTP Execution Time: #(GetTickCount() - starttick)#<br><br>
<cfset starttick = GetTickCount()>
<cfloop from="1" to="20" index="i">
<cf_testtag u="#i#"><br>
</cfloop>
CustomTag Execution Time: #(GetTickCount() - starttick)#<br><br>
</cfoutput>
testtag.cfm
<cfoutput>The ID entered was #attributes.u#</cfoutput>
http.cfm
<cfoutput>The ID entered was #url.u#</cfoutput>
Results (in milliseconds)
Each test was 20 passes at HTTP and 20 Passes at the custom tag.
CFHTTP Tag
661ms 6ms
1624 5
616 5
460 4
522 6
816 4
You can do this by using cfhttp also
<cfquery name="GetUIDs">
select userid from users
</cfquery>
<cfloop query="GetUIDs">
<cfhttp url="http://localhost:8500/cf10/test.cfm?id=#userid#" method="get" result="test">
</cfloop>

How to access the URL parameter id in Coldfusion if it's after a hashtag #?

After trying for about an hour without success... (coldfusion8) a dummy question, but I*m stuck:
My URL (Jquery Mobile, no pushstate, that's why it looks like it is):
http://www.page.de/test/mem/search.cfm#/test/mem/search.cfm?id=9900000003869
If I output:
<cfdump output="e:\website\dump.txt" label="catch" var="#url#">
I get this:
catch - struct
ID: 9900000003869
But how do i access it... I'm trying forever, nothing works:
<cfdump output="e:\website\dump.txt" label="catch" var="#id#">
<cfdump output="e:\website\dump.txt" label="catch" var="#ID#">
<cfdump output="e:\website\dump.txt" label="catch" var="#url.id#">
<cfdump output="e:\website\dump.txt" label="catch" var="#url.ID#">
<cfdump output="e:\website\dump.txt" label="catch" var="#StructGetValue(url,"id")d#">
...
Thanks for helping!
Ok... This works:
URL = http://www.page.de/test/mem/search.cfm#/test/mem/search.cfm?id=9900000003869
<cfset objRequest = GetPageContext().GetRequest() />
<cfset strUrl = right( objRequest.GetRequestUrl().Append( "?" & objRequest.GetQueryString() ).ToString(), 13)>
Credit
If someone finds an easier, please post. I will check as answer.
You're trying to read this from a txt file?
Can you not simply use:
<cfdump label="catch" var="#url.id#" />
Does that work?
EDIT:
Could you try capturing and formatting what you need first then after, writing it to the file?
For example, try using:
<cfsavecontent variable="myFileContents">
<cfoutput>#url.id#</cfoutput>
</cfsavecontent>
<cffile action="Write" file="e:\website\dump.txt" output="#myFileContents#" />
I have not tested this code, but give it a go and see!
Might want to put a check on that URL variable too using isDefined()
Good luck.
Doing some research on fragment identifiers (which is a new term to me :( )prompted by Peter and Duncan's comments, I've found from wiki: http://en.wikipedia.org/wiki/Fragment_identifier
The fragment identifier functions differently than the rest of the
URI: namely, its processing is exclusively client-side with no
participation from the server — of course the server typically helps
to determine the MIME type, and the MIME type determines the
processing of fragments. When an agent (such as a Web browser)
requests a resource from a Web server, the agent sends the URI to
the server, but does not send the fragment. Instead, the agent waits
for the server to send the resource, and then the agent processes the
resource according to the document type and fragment value.
now, being your client IS sending the fragment and the url variable is accessible to you for some reason, using it is done by my original post to follow.
<cfoutput>
is generally how you output a variable or other evaluations to the screen.
<cfset myName = "Travis">
<cfoutput>Hello, my name is #myName#</cfoutput>
You can also access the variable by using it in a statement that doesn't output anywhere.
<cfset myFullName = myName & " Mak">
You can also use the variables in a query
<cfquery name = "qSomeQuery" datasource = "#application.dsn#">
select * from table where id = #url.id#
</cfquery>
However, that's the bad way to use it in a query, you should always use cfquery param.
<cfquery name = "qSomeQuery" datasource = "#application.dsn#">
select * from table where id = <cfqueryparam cfsqltype="cf_sql_integer" value="#url.id#">
</cfquery>
The problem you're having in testing the variable is due to incorrect syntax.
<cfif isDefined("url.id")> verses <cfif isDefined(url.id)> a more accurate test is <cfif structKeyExists(url, "id")>
For some reason my CF server truncates everything in the url after the # but yours doesn't seem to have this problem. As your cfdump states, you can see your url variables so "accessing" the url variable is as easy as using it: #url.id# or testing it <cfif isDefined("url.id")>