Is it possible to make caches_action work for a rails controller method accessed via HTTP POST? - ruby-on-rails

I have a controller action which could benefit from caching. However, when I turn on action caching via the usual:
caches_action :myaction, :expires_in=>15.minutes
...caching doesn't get invoked. It looks like this is because the action is invoked using an HTTP POST. For similar actions invoked using HTTP GET, caching works fine.
I realise using a POST for this action is probably not great style and breaks resource routing conventions - presumably this is also why the response isn't being cached, even though it could be. However for now I'm stuck with it as this is what the client currently does and I can't change it easily.
So, is there a way to force caching for this method even though it is accessed via POST?
edit: I should clarify perhaps that the POST has no side effects, so it is safe to cache the action. It really should have been a GET in the first place, it just isn't and can't easily be changed for now. Also it does not matter for this that browsers or proxies won't cache the response.

Are the contents of the post data the same every post? I suspect they arent and this is why the action wont cache.
A couple of ways to deal with this:
1) Forget about caches_action and use Rails.cache easily inside your controller to cache the expensive parts of your controller code
2) Use Rack Middleware/ Metal Endpoint to receive the post data from the other application and shoehorn the data to the shape you want.
edit:
Im running Rails 2.3.3 and i can confirm that it does cache POST requests
For the purpose of checking while your developing make sure you have set perform_caching to true in development.rb :
config.action_controller.perform_caching = true
Also make sure its the same in production.rb
I tested this scenario with the following in my controller :
caches_action :index
def index
#listings = Listing.find(:all)
end
Using both GET and POST requests this cached as expected.
Also i tried setting the http headers Cache-Control: no-cache on my post client and the action still cached
If you're running OSX use this awesome tool http://ditchnet.org/httpclient/ to create GET and POST requests

Related

ruby rails web request response lifecycle

I'm a novice in ruby on rails trying to understand the in-depth flow of a typical request/response life cycle in ruby on rails web application.
I have googled for the info and have not found an answer which is complete or detailed to the level of DNS servers to dispatchers.
The closest I got to a good explanation is at:
http://brainspl.at/request_response.pdf.
Can someone point me to a better or more detailed explanation?
-Raviteja
So you are asking for rails request/response cycle and you already referred to a presentation which really describes it very well. So im assuming that you want to know it from a very high level and you need this concept totally for development. Then here it is. Im just trying to name the parts sequentially.
Route: Here you will draw the paths which will be used by the world to access your application. With a complete RESTful architecture, you need to define the hierarchy of your resources and define how a resource can be accessed to perform some action. If any request to your application doesnt match with any path in the routes file, it will not be processed. If any match occurs, it will find the corresponding controller and action and will call it. At the time of calling, it will store all the request related data in params hash.
Before Filters: Now your application already know which controller#method is gonna process the request. And it will check if there is anything configured to execute before calling that method. This is done by using before_filter. If found anything then those functions will be called first.
Method Execution: After executing all the before_filter methods in a particular sequence, it will call the actual method. All the data is available in params hash in this method. It processes input data, invokes Model calls for database access, and prepare data for view.
View: Proper view file will be chosen based on the controller#action, format. Or you might select any particular view to render by render :partial call. And the response will be prepared using the variables prepared in controller. This response will go to the client.
After Filters: After processing the view, it will look after_filter methods and will those if found.
Well this was a quick overview i would say, without really any details. Im saying again, the pdf you referred really contains more details.
Let me know if you want to know anything more specifically.
A user opens his browser, types in a URL, and presses Enter. When a user presses Enter, the browser makes a request for that URL.
The request hits the Rails router (config/routes.rb).
The router maps the URL to the correct controller and action to handle the request.
The action receives the request, and asks the model to fetch data from the database.
The model returns a list of data to the controller action.
The controller action passes the data on to the view.
The view renders the page as HTML.
The controller sends the HTML back to the browser. The page loads and the user sees it.
https://www.codecademy.com/articles/request-response-cycle-dynamic
and https://www.codecademy.com/articles/request-response-cycle-forms
Everything starts when ‘url’ requested by a user. The browser needs to know sever’s IP address to connect, So it lookup DNS(Domain name system) which translate your domain into the public IP address of the particular server. Then the Browser will do threeway handshake to connect server like puma in port 80. And decide upon public and private key it happen only because if url use HTTPS. HTTPS is a secure wrapper around HTTP and TCP. Then Server triggers the rails application through middleware like rack and provides request verb, header, body to the application. Then rails application use Journey (Default route library of rails) to find the consent controller and action which matches the request and call with the request and params.
Then rails lifecycle callbacks like before, after, around will be triggered during the process. The action takes care of requesting data from the model and rendering the consent view for the request. Finally sent back the status, header, and body as the response.
If you want to learn in-depth about lifecycle, check this article The Lifecycle of a Request
It is also important to note that Rails apps use an MVC architectural pattern, which is Model, View, and Controller at a high-level the life-cycle of a request in rails app is simply the interaction of the Model, View, and Controller. This article gives you an overview.

Redirecting a GET request to another app's POST request in a rails controller

I have a controller action that is triggered by a GET route, which under some conditions, I want to redirect to the POST route of another app on another server.
I'm aware of redirect_to "http://somewhere.com/thing_to_post_to" but there doesn't seem to be a way of specifying the http method. This intended for AJAX requests not pages, so it isn't acceptable to return a javascript to direct the browser.
Is this possible?
redirect_to uses HTTP status code to make the redirection, so it would likely be a GET, here's antoher explanation to use 307 status code
Response.Redirect with POST instead of Get?
but is not very well implemented among all browsers, so i suggest using a session variable to pass parameters among the views or something similar.

Setting HTTP Headers for Rails form_for

I am currently working on an avatar app powered by Rails where users can upload avatars for their user profile.
I would like to use a custom HTTP header to block public upload requests and only allow requests from my apps. How would I go about doing this with Ruby on Rails?
I am uploading the avatars using AJAX so this may be a bit harder. Also I would prefer not to show the header in the public HTML code otherwise it defeats the object of adding it!
If you add
protect_from_forgery
to your application controller, it will block all NON Get requests from 3rd party links. It will add a hidden input value to each form with an authentication token that will be used to check all data that is sent to the servers.
Further reading
http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf
http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html
Rails 3.1 - CSRF ignored?
You could implement a custom HTTP header (say X-Foobar-Validity-Status: valid) and check it in a before_filter.
class YourController < ApplicationController
before_filter :check_header
def check_header
unless request.headers['X-Foobar-Validity-Status'] == "valid"
render json: {"error" => "You are an evil attacker. Go away"}
end
end
end
However, I would consider this a bad idea.
Attackers can read the packet dump of your HTTP requests and add the headers, even with jQuery. See the jQuery.ajax headers option.
Instead of using a proprietary header, I would use User-Agent for this purpose.
Instead, I would sugest using the protect_from_forgery mechanism of rails. It makes your life easier and is more secure. Just fetch the authenticy token by a http request in your app and then send it back with your request. This should keep intruders out.

Rails POST doesnt extract any path, query or request parameters

I want to grant users access to my API (hosted on heroku.com) from their sites.
But a strange problem occurs, when i want them to allow to post to the api:
Data sent from an correct form with the correct action-url (e.g. "http://myapp.com/projects/123/tasks/321/todos") - the params get serialized and send via jQuery - i encounter an "ActionController::MethodNotAllowed" with the additional info: "Only get and post requests are allowed", that re-routes to ApplicationController#index with :method => :options.
Rails doesnt extract the params for project_id (123) and task_id (321) from the url, neither are any further request_parameters, path_parameters or query_parameters available.
This behaviour occurs, when I POST from external sites, but doesn't occur, when posting from an html-page on my local machine. My first thought was about wrong encoding, but how to fix that problem.
Edit:
I am using authlogic (not devise :-D) and for the :create action the protect_from_forgery is already skipped.
Any suggestions appreciated
i guess that happens because rails tries to protect your form CSRF attacks.
you can comment out the protect_from_forgery line in your ApplicationController to test it.
but im not sure if thats the right way of dealing with this issue in the production environment.
Okay. I'll try and answer the right question this time (see other comment).
So I've thought about this, and I'm wondering, is this a case of the jQuery call attempting a PUT request? When you use the local form Rails will POST the data, but add the extra _method field to emulate a PUT.
Don't know if you are using jquery-rails, but this takes care of setting the _method parameter, and the PUT/POST verb translation for you in your AJAX calls.
The problem occured due to the cross domain policy - the request was made from another domain - and because I was using a recent browser that supports CORS, it was sending an OPTIONS-request first to get known from the server, which actions are allowed for this domain.

Rails, REST Architecture and HTML 5: Cross domain requests with pre-flight requests

While working on a project to make our site HTML 5 friendly, we were eager to embrace the new method for Cross Domain requests (no more posting through hidden iframes!!!). Using the Access Control specification we begin setting up some tests to verify the behaviour of various browsers.
The current Rails RESTful architecture relies on the four HTTP verbs: GET, POST, PUT, DELETE. However in the Access Control spec, it dictates that non-simple methods (PUT, DELETE) require a pre-flight request using the HTTP verb OPTIONS. In addition during testing we discovered that Firefox 3.5.8 pre-flight POST requests as well.
My question is this. Is anyone aware of any project for the Rails framework working to address the issue? If not, any opinions about the best strategy to support the OPTIONS method, since it has to support the routes for all the POST, PUT, DELETE methods?
I released a Gem a couple of days ago that implements CORS support via a Rack Middleware:
http://github.com/cyu/rack-cors
Regarding preflight CORS requests, I couldn't get preflight requests working in Chrome (through simple CORS requests work fine). Searching around the Internets suggests that it might not be supported. I've asked questions in the Chrome forum about this, but haven't heard a response yet.
This is from Spine js documentation
CORs Rails integration
Let's create a cor method, which will add some of the request access control headers to the request's response.
Add the following to app/application_controller.rb:
before_filter :cor
def cor
headers["Access-Control-Allow-Origin"] = "js-app-origin.com"
headers["Access-Control-Allow-Methods"] = %w{GET POST PUT DELETE}.join(",")
headers["Access-Control-Allow-Headers"] = %w{Origin Accept Content-Type X-Requested-With X-CSRF-Token}.join(",")
head(:ok) if request.request_method == "OPTIONS"
end
Although Access-Control-Allow-Origin takes a wildcard, I highly recommend not using it as it opens up your app to all sorts of CSRF attacks. Using a whitelist is much better and more secure.
The Access-Control-Allow-Headers section is important, especially the X-Requested-With header. Rails doesn't like it if you send Ajax requests to it without this header, and ignores the request's Accept header, returning HTML when it should in fact return JSON.
It's worth noting that jQuery doesn't add this header to cross domain requests by default. This is an issue that Spine solves internally, but if you're using plain jQuery for CORs, you'll need to specify the header manually.
jQuery.ajaxSetup({
headers: {"X-Requested-With": "XMLHttpRequest"}
});
Some browsers send an options request to the server first, to make sure the correct access headers are set. You'll need to catch this in Rails, returning a 200 status with the correct headers. To do this, add the following to your application's config/routes.rb file:
match '*all' => 'application#cor', :constraints => {:method => 'OPTIONS'}
That's it, you're all set up for Cross Origin Requests with Spine!
I hacked rails to support the options method. I posted this on the rails list, but it never made it past the list.
GitHub Gist: Rails XHR2 / CORS / OPTIONS support
ctrl+f to find the lines that have #Options - those are the only ones I changed.
And here's an example implementation | and another

Resources