| | 1 | = Search Engine Safe (SES) Urls = |
| | 2 | |
| | 3 | There is a school of thought that traditional URLs for dynamic web applications are a disadvantage when being ranked by search engines such as Google. The idea is that search engines look for a question mark in the URL and assume the page is dynamic. Thus, the content on that page is less relevant than similar content on a purely static page. |
| | 4 | |
| | 5 | A common SES URL might look like this... |
| | 6 | |
| | 7 | {{{ |
| | 8 | http://yourdomain.com/index.cfm/variable1/value1/variable2/value2 |
| | 9 | }}} |
| | 10 | |
| | 11 | ...instead of this... |
| | 12 | |
| | 13 | {{{ |
| | 14 | http://yourdomain.com/index.cfm?variable1=value1&variable2=value2 |
| | 15 | }}} |
| | 16 | |
| | 17 | As you can see, rather than using a question mark to denote the start of the collection of URL variables, and an ampersand to denote each name/value pair, and the equal sign to denote the value of each variable, the SES URL simply adds a front slash after the index.cfm and instead of the equals and ampersand characters. |
| | 18 | |
| | 19 | The entire URL can be accessed from various CGI variables. The exact variables differ between web servers and are beyond the scope of this document to identify. |
| | 20 | |
| | 21 | To implement this type of SES URL scheme, a developer would add a block of code into their application.cfm or application.cfc that would parse the URL values from CGI scope and manually set them into URL scope for that request. Model-Glue would then use the URL variables, as if they were any other URL variable, and place them in the Event and !ViewState objects. |
| | 22 | |
| | 23 | This technique works just fine, but has a couple of side effects that the developer needs to be aware of. Firstly, if you're using Model-Glue, you can't simply use the "myself" value from the event or viewstate objects. The "myself" value includes the path to the index.cfm as well as a question mark, the name of the event variable and an equals sign. For example, "index.cfm?event=". Obviously, this doesn't work very well for SES URLs. As a result, developers would be forced to manually build the URL. |
| | 24 | |
| | 25 | The other problem is that all resources being used on the website need to be have their urls be root relative. This is because the browser thinks that the user is in a subfolder somewhere underneath the root. For example, if the SES URL was "/index.cfm/variable1/value1/variable2/value2", and you had an image whose source was "images/foo.png", the browser would look for that image under "/index.cfm/variable1/value1/variable2/value2/images/foo.png". To resolve this problem the developer needed to set the path to all resources to start with "/" which tells the web browser to look under the web root. So, setting the image path to "/images/foo.png" would resolve the problem. |
| | 26 | |
| | 27 | Another technique that has commonly been used is URL Rewriter plugins. Apache provides Mod_rewrite and there are several options available for IIS, the most common being ISAPI Rewrite. All of these URL Rewriters work in the same basic manner. They use a configuration file that is external to your CFML code to control how the system will parse the URL and notify your application of the URL variables. As with the manual parsing technique, URL Rewriters require you to link all your application's resources from the webroot. |
| | 28 | |
| | 29 | Model-Glue 3 includes support for the first technique of parsing SES URLs. |
| | 30 | |
| | 31 | == Using the Event's linkto() Function == |
| | 32 | |
| | 33 | Before we get into how to use the SES URL features in Model-Glue 3, let's discuss some features that support both SES URLs and normal URLs. |
| | 34 | |
| | 35 | In Model-Glue 3, the viewstate object has been deprecated. In previous versions of Model-Glue you would use the Event object within your controllers and any values set into the Event would be copied into the Viewstate object that would be used within the view. These were two different objects that served, to a degree, the same purposes. In Model-Glue 3, the Viewstate still exists but only for backwards compatibility. In new applications you should use the Event object within your views to get values from the event. |
| | 36 | |
| | 37 | The new Event object has a function on it, linkto(), that you can use to build links within your application. This function replaces the traditional use of the "self" any "myself" viewstate variables. So, within the view, you would create a link like... |
| | 38 | |
| | 39 | {{{ |
| | 40 | <a href="#event.linkTo("user.profile", "userId")#">Example Link</a> |
| | 41 | }}} |
| | 42 | |
| | 43 | ...rather than... |
| | 44 | |
| | 45 | {{{ |
| | 46 | <a href="#myself#user.profile&userid=#userId#">Example Link</a> |
| | 47 | }}} |
| | 48 | |
| | 49 | The linkto function accepts two arguments. The first is an event to link to. In the example above, we're linking to the "user.profile" event. The second argument is a comma delimited list of values to append to the URL from the event. In the example above we're going to append the userId value from the event to the URL. Assuming you're using the default Model-Glue configuration and that you have a userId value in the event (for the sake of this documentation, let's say the userId is 12), the generated HTML would look like this... |
| | 50 | |
| | 51 | {{{ |
| | 52 | <a href="index.cfm?event=user.profile&userId=12">Example Link</a> |
| | 53 | }}} |
| | 54 | |
| | 55 | What if you want to output a list of users, each with a different userid? This is common when you want to list a summary page with a link to details for each user. You simply use event.setValue('userid',userid) inside the query output. Your code would look like the following: |
| | 56 | |
| | 57 | {{{ |
| | 58 | <cfoutput query="users"> |
| | 59 | <cfset event.setValue('userid',users.userid) /> |
| | 60 | <a href="#event.linkTo("user.profile", "userId")#">#users.username#</a> |
| | 61 | </cfoutput> |
| | 62 | }}} |
| | 63 | |
| | 64 | Behind the scenes, the linkto() function uses a !ColdSpring bean named "modelglue.urlManager" to create the link. This object is configured, by default, in the Model-Glue core configuration file to use the modelglue.gesture.eventrequest.url.!UrlManager component. This component assumes it is managing standard URLs. |
| | 65 | |
| | 66 | Due to how Model-Glue is configured using !ColdSpring, developers can override this bean within their own !ColdSpring configuration file and use any other bean they specify. For convenience sake, Model-Glue includes a !ModelGlue.gesture.eventrequest.url.!SesUrlManager which you can use to create (and parse) SES URLs. |
| | 67 | |
| | 68 | To enable SES URLs using this built in object, simply add this XML to your application's !ColdSpring configuration file: |
| | 69 | |
| | 70 | {{{ |
| | 71 | <bean id="modelglue.urlManager" class="ModelGlue.gesture.eventrequest.url.SesUrlManager"> |
| | 72 | <property name="modelGlue"> |
| | 73 | <ref bean="modelglue.ModelGlue" /> |
| | 74 | </property> |
| | 75 | </bean> |
| | 76 | }}} |
| | 77 | |
| | 78 | It's just that simple! Once you have that XML in place you can simply reload your application and the new urlManager will cause the linkto() function to create SES links and your application will know how to parse them! |
| | 79 | |
| | 80 | Because of the pluggable nature of the urlManager, you should now use the event.linkto() function to create all links within your application. It's also a good practice to make sure all links to CSS, Images, Scripts, or any other asset are root relative, even if you're not using SES URLs. This will give you the flexibility to turn SES URL parsing on or off for your entire application at the flip of a switch. |
| | 81 | |
| | 82 | == Creating your own URL Manager == |
| | 83 | |
| | 84 | Model-Glue 3 comes with two urlManager objects, a standard urlManager and a !SesUrlManager. But, what if you don't like the formatting of the SES URLs created by the !SesUrlManager? For example, let's say you prefer an alternative format for your URLs? |
| | 85 | |
| | 86 | The answer is quite simple. All you need to do is create your own urlManager by either extending the !ModelGlue.gesture.eventrequest.url.!UrlManager object or implementing its interface separately. You should note that this object doesn't actually use a cfinterface, instead you simply need to have a set of functions with the same name, arguments and return types and duck typing will take care of the rest. |
| | 87 | |
| | 88 | Once you've created your custom urlManager you can wire it in using !ColdSpring the same as you did for the !SesUrlManager, but replace the class attribute with the class name for your custom urlManager. |
| | 89 | |
| | 90 | One thing you should be aware of when extending classes in Model-Glue is that the API for these objects may change or the objects themselves may be removed in future revisions of the framework. So, if you do implement your own urlManager you may need to edit or re-implement this code to move to future versions of Model-Glue. |