William asked:
How do you tell jquery in an autocomplete to remove the leading // from the json response? Do you need to use an event function to manually do it, or is there a simple way? This prefixing of JSON responses is mandatory for one of my projects.
Yes, you do need an event function to manually do it, but luckily jQuery makes this rather simple. To test this, I began with a very simple autocomplete implementation. This example uses ColdFusion on the back end but obviously it would work with any application server. First, here is the front end:
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.min.js"></script>
<script src="jquery-ui-1.8.16.custom/js/jquery-ui-1.8.16.custom.min.js"></script>
<link rel="stylesheet" href="jquery-ui-1.8.16.custom/css/vader/jquery-ui-1.8.16.custom.css" type="text/css">
<script>
$(function() { $("#name").autocomplete({
source: "source.cfc?method=searchart&returnformat=json"
}); });
</script>
</head> <body> <form action="" method="post">
name: <input name="name" id="name" />
</form> </body>
</html>
<html>
And here is the back end. Again - this could be any language at all.
remote function searchArt(string term) {
var q = new com.adobe.coldfusion.query();
q.setSQL("select artname from art where lcase(artname) like :term");
q.addParam(name="term",value="%#lcase(arguments.term)#%",cfsqltype="cf_sql_varchar");
var query = q.execute().getResult();
var result = [];
for(var i=1; i<=query.recordCount; i++) {
result[arrayLen(result)+1] = query.artname[i];
}
return result; } }
component {
This example makes use of jQuery UI's Autocomplete controls, one of my favorite parts of jQuery UI. As you can see it is rather simple to use. I simply tell it which input field I want to turn into an autocomplete and tell it how to fetch the data. (There are many more options but for now, this is all we need.) This runs fine until we begin to use the feature William spoke of - prefixing the JSON result with two / characters in front. This is used in some situations to help make the Ajax application a bit more secure. By prefixing the JSON string we require preprocessing on the string before blindly evaluating it. While how this is done will depend on your application server, in ColdFusion you can turn this on at the server level or on a per application basis. Here's how I did it for this demo:
this.name="autocompleteprefixissue";
this.datasource="cfartgallery";
this.secureJSON="true";
this.secureJSONPrefix="//"; }
component {
Once this feature is enabled, your autocomplete stops working. You get no error. You're really stuck. If you know enough to check the network monitor, you can see the issue:
So how do you fix it? One of the more cooler features of jQuery is the ability to register global Ajax handlers. But we're going to tackle this a bit differently and instead use ajaxSetup. This is similar but works at a slightly lower level. One of the features is called dataFilter. As you can guess, it allows you to filter the data returned by Ajax requests. What's cool is that this happens behind the scenes. Our autocomplete code will never know it even occurred. Let's look at how it works.
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.min.js"></script>
<script src="jquery-ui-1.8.16.custom/js/jquery-ui-1.8.16.custom.min.js"></script>
<link rel="stylesheet" href="jquery-ui-1.8.16.custom/css/vader/jquery-ui-1.8.16.custom.css" type="text/css">
<script>
$(function() { $.ajaxSetup({
dataFilter: function(data, type){
return data.substring(2, data.length);
}
}); $("#name").autocomplete({
source: "source.cfc?method=searchart&returnformat=json"
}); });
</script>
</head> <body> <form action="test4.cfm" method="post">
name: <input name="name" id="name" />
</form> </body>
</html>
<html>
My dataFilter function takes in the data stream and simply removes the first two characters. As I said, nothing in autocomplete was changed. As soon as this is added, things begin working perfectly again. Try this yourself below (and look - I didn't put any pesky console.log messages so all your poor folks in older IE builds or Firefox w/o Firebug can enjoy it!).
Archived Comments
Maybe this is the wrong way to go about it, but you can turn off JSON prefixes in the CF admin, wouldn't that achieve the same result?
Right, but in this case, the prefix is out of the end user's control. (I don't think he is even using ColdFusion.)
There's a missing file -
File not found: /demos/2011/nov/8/test4.cfm
Where do you see that link?
Oh, you submitted the form. Don't do that. ;) I removed it from the action in the source above - not from the demo.
Great info - can you explain what the JSON has to be prefixed - I've heard about doing this and have similar requirements with some clients. It makes sense in a way that the data feed can't just be thrown on the right hand side of an equals sign and "executed" as an expression; that could make the browser vulnerable to an exploit if a nasty expression got injected (if the Ajax call got hijacked) So it seems to me that the setup function should do more than just remove the prefix, but should also (first) validate the data is 1) from where it is supposed to be and 2) is structurally sound. but I'm speculating. Any thoughts on this?
I'm not sure if you _could_ detect a network hijack of some sort. It would seem impossible to me!
Hi Ray,
Good post. This is probably slightly better code to use:
$.ajaxSetup({
dataFilter: function(data, type){
return type == 'json' ? data.replace(/^(\/{2})?/, '') : data;
}
});
Just to ensure that other response types are not messed with and the // is only removed if it exists - otherwise if that setting was updated for whatever reason it would removed part of the real response.
-s
@Shaun: Nice! Yeah, I agree that makes more sense.
No problem
I just started wrestling with this today as I'm working on converting a large site from CF 8 to CF 10. Where my code is all CF I've got my modifications in place. However where it's jQuery with the validate plugin I'm having a problem. I can't see any indication that the ajaxSetup() is doing anything to the response. I put the relevant jQuery code up at pastebin (http://pastebin.com/Gu21uQZ8). I'm guessing I'm going about this completely wrong?
As far as I know, ajaxSetUp is *global*, so every plugin that uses XHR should use it. Your code looks right to me. Is this online where I can run it?
I just found a blog post that indicated that jQuery Validate may break ajaxSetup. I'm trying to confirm now.
I think it may be the validate plugin. Try copying dataFilter argument to the remote block.
Thank you, thank you, thank you! While I did need the information about the json prefix (thank you!), what really saved my day was the comment about the network monitor. I've been using the Chrome developer resource for years, but never drilled far enough down in that panel to see that it returned the information I needed.
I had been having trouble with a CFC that was being called with jQuery and was supposed to reurn json, and it worked fine on my local system, but wasn't working at all on the remote server. I had written debug info into the CFC code at strategic spots to be written to a file, but the file wasn't being written either, and I was flying blind and getting very frustrated.
But after seeing your reference to the network monitor I was able to have the component return a comprehensive error message in that window. Thank you SO much! You have saved me from hours of hair pulling. You're awesome. Have a great day!
Glad it helped. Check out this other resource for learning about DevTools: http://www.raymondcamden.co...