Frontier Tutorials / Writing an ObjectNotFoundHandler / Misdirected URLs

Misdirected URLs

Why Do We Get Bad URLs?

The Mode 1 scripts described in the Mode 1 Example can result in incorrect URLs being generated on the page. In fact, since the Master ONFH handler described in Mixing Modes defaults to function as a similar Mode 1 script, it will also exhibit this behavior.

This happens when the objectNotFoundHandler is at a different level than that which The Walk reached before giving up and rendering the objectNotFoundHandler.

To see why this is so, let's posit a website with this structure:

	#objectNotFoundHandler = @theSite.["#tools"].PageNotFound
		PageNotFound [script]

And let's see what happens when the requested URL is http://theSite/subs/notThere.

The Walk terminates at @theSite.subs when mainResponder.respond fails to locate an entry named notThere. But it finds your #objectNotFoundHandler in the page table. So mainResponder.respond puts lastNomad=@theSite.subs and remainingPath="notThere" in the page table, and calls the ONFH script.

The ONFH script does its thing, and returns the expected text, which the website framework inserts into the template. But when the template's navbar macro is expanded, the information in the page table indicates that it's at the top level of the website (where the #objectNotFoundHandler appears) rather than inside the subs subdirectory, and generates the relative links accordingly.

The browser then interprets these relative links as being relative to the page that was returned, which appears (to the browser) to be at http://theSite/subs/notThere. Voila--Bogus URLs!

We'd see the same behavior if the quoted string (would-be link) to "Site Outline" were expanded.

What Can We Do About It?

The basic problem is that the ONFH handler isn't necessarily in the same table or subtable that The Walk reached before giving up and rendering the ONFH handler. To resolve the problem, therefore, we need to make sure that the page table reflects the actual location of the ONFH handler.

The simplest solution to this is to not use a Mode 1 ONFH. Instead, simply redirect to another URL within the website, such as a search page. This is the basis of Mode 2 ONFH scripts, as described in the Mode 2 Example.

However, it is possible to fix this within a Mode 1 ONFH.

The easy and obvious fix isn't available to us: we could simply set adrPageTable^.adrObject to the address of an existing page entry within the table pointed to by adrPageTable^.lastNomad. This would take care of most of the bad URLs, because most (if not all) scripts that generate relative URLs read the current page address from adrObject. However, in scripts such as the navbar macro, this would result in an un-linked entry instead of a real, linked entry.

There is a fix, but it takes us halfway to Mode 2.

The Fix

The technique described here to fix the misdirected-URLs problem was devised by Matt Neuburg, and presented in his Dr. Matt article The ObjectNotFoundHandler. It is included here for completeness.

The fix is to redirect to a nonexistent page in the same table/directory as the #objectNotFoundHandler. This will result in another rendering of the #objectNotFoundHandler, but from the directory in which it resides. Once we have done that, the relative links will be properly rendered.

Let's work with the with the master ONFH script from Mixing Modes, since that gives us the greatest leverage. The following is the master script after modification.

« ObjectNotFoundHandler Master Script
space picturepageTbl = html.getPageTableAddress()
space picturetoolsTbl = pageTbl^.tools
« Parse searchArgs (if any) into temp args table
local ( tempArgs )
new ( tabletype, @tempArgs )
webserver.parseargs( pageTbl^.searchargs, @tempArgs )
if defined( tempArgs.whatObjectNotFound ) « Is this the redirected call?
space picture« This is the redirected call; make Mode 1 return.
space picturepageTbl^.title = "Page Not Found"
space picturepageTbl^.code = 404
space pictureif defined( pageTbl^.notFoundText )
space picture« Put missing subpath in page table for inclusion in #notFoundText
space picturepageTbl^.whatObjectNotFound = string.nthField( tempArgs.whatObjectNotFound, "/", 1 )
space picturereturn ( html.processMacros( string( pageTbl^.notFoundText^ ) ) )
space pictureelse
space picturescriptError ("Can't process the request because there is no object named \""
space picture+ string.nthField( tempArgs.whatObjectNotFound, "/", 1 ) + "\".")
space picturetoolsTbl^.PageNotFound( pageTbl )
bundle « Mode 2 & 3 ONFH scripts
space picture« Mode 2 & 3 scripts are assumed to return if unsuccessful, but not if successful.
space picture« As a result, we can simply call them one after another.
space picturetoolsTbl^.ReturnBinaryObject( pageTbl )
« URL not handled by Mode 3 scripts; do Mode 2 redirect so we can do Mode 1 return without bogus URLs.
space picturemyDepth = sizeof(string.parseaddress(this))
space picturenomadDepth = sizeof( string.parseaddress( pageTbl^.lastnomad ) )
space picturetargetDepth = string.countfields(pageTbl^.remainingpath, "/")
space picturegoUpLevels = nomadDepth + targetDepth - myDepth
space picturerelPath = string.filledstring( "../", goUpLevels ) + "nonexistent"
scripterror ("!redirect " + relPath + "?whatObjectNotFound=" + string.urlEncode( pageTbl^.remainingPath ) )

There are three major blocks in the revised script.

The second block is unchanged from the original script. It simply calls any number of Mode 2 & 3 handlers.

The third block does a Mode 2 redirect back to the #objectNotFoundHandler (actually, to a nonexistent page in the same table as the #objectNotFound handler, but the effect is the same). It tacks the missing subpath onto the URL as a search argument.

The first block checks for the presence of the "whatObjectNotFound" field in the searchArgs. If it finds the field, it generates and returns a page-not-found page (Mode 1), as in the expanded script in the Mode 1 Example.

The third and first blocks, in combination, fix the misdirected-URLs problem.

Now let's review what we've covered.

Tutorial Contents
Writing an ObjectNotFoundHandler
ONFH Overview
ONFH Modes of Operation
About The Examples
Mode 1 Example
Mode 2 Example
Mode 3 Example
Mixing Modes
Misdirected URLs
ONFH Summary
ONFH Applications
ONFH Resources
Bonus: The "Penultimate" Master Script
Bonus: Mode 3 Utility Script
About the Author