Simulating XML-formatted requests with a check box 11

I was working on the search form shown above in the usual RESTful manner. This form posts to a create action of my SearchesController, and the code basically looks like this:
def create
@search_results = find_matching_rows # code to search the db
respond_to do |format|
format.html # default behavior, show results in html page
format.xml { render :xml => @search_results.to_xml }
end
end
My routes file looks like this:
map.resources :searches map.resources :customers
If you've been doing any REST lately, the above code should look familiar.
The part I want to talk about today, though, is the checkbox you see at the bottom of the form.
If the user fills in some search criteria and leaves the box unchecked, they should get a normal search results view. But if they check the box, I want to return the results as XML. Why would I do this? Well, in my case, it's just so I have an easier way to check my respond_to logic (yes, yes, I already have functional tests for it, but ignore that for the moment) since it's nice to be able to see the XML come back in the browser (in Firefox, anyway... Safari doesn't seem to do this for me, I have to view source afterwards.)
Rails (1.2 and higher) support the notion of a "format" parameter, which can be automatically recognized if you're using map.resources or something like map.connect ':controller/:action/:id.:format'. You can go directly to an address like localhost:3000/customers/1.xml and Rails will set params[:format] to xml. This, in turn, is what activates the format.xml code in the respond_to block above.
However, in my case, I have a a form that I'm using to search the database. So I added this code to my form to display the checkbox:
<p><%= check_box_tag 'format', 'xml' %>XML</p>
check_box_tag is useful when you have a checkbox that can't be mapped to any attribute of your ActiveRecord model. The first argument will become the key in the params hash in your controller; and the second will become the value (there are more parameters and options you can specify, I always have to look them up every time).
The trick here is, by specifically using "format" and "xml" as the values to for the checkbox tag, we will simulate the exact values the params hash needs to make the respond_to block to work. The user checks the box, clicks Submit, and presto - XML in their browser (well, not in Safari unless you view source, but Firefox seems to know what to do).
Cool, no?




This is something I've been curious about.
When I see input from a checkbox used like that in code, my instant mental response is, "But what if someone injected an arbitrary value into that checkbox's input?" For instance, if they re-wrote the client-side code to have it inject some other format when the form is submitted. How does the code handle that?
I'm curious because in Rails I often see input from the user being treated in an almost cavalier way. But, I can never tell when that's totally fine to do and when it's just the developer not paying attention.
After many years of ASP.Net programming, I have developed a rabid fear of ANY input from the the user and treat any user input first and foremost as if it were a malicious attack on the site. I just ended up coding with zero assumptions that the form data being submitted to me had anything to do with the actual form I had coded and provided to the user to submit the data.
I guess the short of it is, I have a hard time being able to recognize when Rails is being helpful with user input, along the lines of "convention over configuration", and when it's up to me to ensure everything from the user is thoroughly sanatized.
Having come from the ASP.Net world, is this something you have run into yourself? Does anyone else reading this blog have these same questions?
I am interested in your decision to create a separate search controller vs. integrating the search into your customer controller. What was you reasoning for that design decision?
@ Nicholas
I'd imagine because you can use it to search multiple models instead of just one.
Nicholas: As Daniel points out, it's a way for me to centralize all of my search functionality regardless of the model I'm searching.
But also, I don't know of a REST-ful way of integrating search into an already-restful resource. I'd have to add some custom actions in addition to the seven predefined ones, which I tend to avoid unless there's just no other choice.
Plus, it makes it easy to fulfill requirements like "what's our most popular search", and "what were the last 10 searches", by thinking of search critiria as a resource unto itself - something worth saving and retrieving later. So that's why I've tended to break search out into its own resource like I've done here.
"But also, I don't know of a REST-ful way of integrating search into an already-restful resource."
I treat search as a subset of index. If there's a q= param (or resource specific queries like name, email etc) I return the subset of the collection instead of the entire collection.
Michael: I think that index should only be called with the GET verb, though. think that's a good way to refine the index action, but it's not suitable for something like the target of a search form, unless you're willing to change the form action from POST to GET, which may or may not be possible depending on the kind of data you've got on the form.
But in general I'm glad you brought it up. Many people don't realize that's perfectly valid to add query parameters to RESTful actions.
@Jeff: I really like this idea a LOT. Thanks for sharing.
I just wanted to comment on GET vs POST for searches:
First, searches as GET often times work better that way (no goofy back-button "page expired" nonsense). Google does searches this way. GETs in REST are reads.
Second, in REST, if you use POST, you're "creating" a resource. That's not really accurate in this case. Now, if you were allowing me to actually create a search I might use again, that's a case for it (and a neat idea too!)
Keep posting. Great stuff!
@Dylan: (saved your comment from the spam filter, that's why it took a while to show up) I think it's always good to be very defensive about all user input. And actually, this is where Rails conventions help. Rails tends to do many things behind the scenes for you to help make your site more bulletproof *if* you're following conventions.
In this case, I'm simply allowing the checkbox to make it easier to access a feature I've already decided to expose - XML results. So no harm there.
The biggest error I've seen among Rails newbies is doing something like this:
product = Product.find :first, :conditions => "name = #{params[:product][:name]}"Not good, right? This is subject to a SQL injection attack. Instead, Rails encourages you to do this whenever you have SQL parameters coming from an untrusted source:
When using the [] syntax, Rails will internally call sanitize() automatically on all of the untrusted data to make sure the SQL calls are safe. (You can also call sanitize manually if you need to.)
That's just one example. So I would say, Rails sites are inherently safer than .NET 2.0 (or earlier) sites because of the built-in behavior that you get for free if you follow best practices. (I don't know as much about .NET 3.0+ yet to say how it compares).
I also use GET with a ?q= param on the index action of an existing REST resource for searches. I like GET in general for things like searches since it makes that exact URL unique to the list of resources being displayed. e.g. /biscuits always shows me the most recent biscuits, /biscuits?q=buttered always shows me only the buttered biscuits.
If I was searching across multiple resources a separate search resource makes sense, though I'd still probably use GET. In fact I'd probably just have an index and a show action, with the search terms as the :id parameter for show... somehow... wow I need to think about this now...
The things in themselves have lying before them, in respect of the intelligible character, the things in themselves, and the transcendental unity of apperception can be treated like natural causes.
The fact is that we must not let ourselves be frightened by considerations of immanent time and noetic acts.