What's the actual URL Rails generated for HTTP Verb PATCH? - ruby-on-rails

Please bear with a newbie. I understand how Rails provides simple request with GET for simple URL links like localhost:3000/rooms/11/listing. The format is straight forward as stated in the Routes table. However I am confuse when it comes to PATCH, PUT, DELETE & CREATE. For example the output below, with params, was when I clicked SAVE button. My question, what's the actual URL that Rails generated when I clicked that SAVE button?
Started PATCH "/rooms/11" for 127.0.0.1 at 2019-08-20 05:25:32 +0800
(0.8ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
↳ /usr/local/rvm/gems/ruby-2.6.0/gems/activerecord-5.2.2/lib/active_record/log_subscriber.rb:98
Processing by RoomsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"JHiCJ6HoTsgd8SGgLTFiZ9+J9hS9U8hHGvjKf4Lz3uieQ8OO2eFqFEM/D5xocHp/Nd3eA0az9k+okrmNe65BYg==", "room"=>{"home_type"=>"Apartment", "room_type"=>"Private", "accommodate"=>"3", "bed_room"=>"4", "bath_room"=>"3"}, "commit"=>"Save", "id"=>"11"}
I know from console
app.room_path(11)
=> "/rooms/11"
Is this Rails generated URL localhost:3000/rooms/11{"utf8"=>"✓", "authenticity_token"=>"JHiCJ6HoTsgd8SGgLTFiZ9+J9hS9U8hHGvjKf4Lz3uieQ8OO2eFqFEM/D5xocHp/Nd3eA0az9k+okrmNe65BYg==", "room"=>{"home_type"=>"Apartment", "room_type"=>"Private", "accommodate"=>"3", "bed_room"=>"4", "bath_room"=>"3"}, "commit"=>"Save", "id"=>"11"}?

No, the URL generated by app.room_path(11) is http://localhost:3000/rooms/11.
PATCH, PUT, DELETE and POST are called HTTP verbs. CREATE is not an HTTP verb.
One of these verbs goes along with your request, and Rails Router uses it to route the request to the correct Controller and Action.
Requests can have parameters, likes the one you showed here:
{"utf8"=>"✓", "authenticity_token"=>"JHiCJ6HoTsgd8SGgLTFiZ9+J9hS9U8hHGvjKf4Lz3uieQ8OO2eFqFEM/D5xocHp/Nd3eA0az9k+okrmNe65BYg==", "room"=>{"home_type"=>"Apartment", "room_type"=>"Private", "accommodate"=>"3", "bed_room"=>"4", "bath_room"=>"3"}, "commit"=>"Save", "id"=>"11"}
When you clicked on the Save button your browser requested http://localhost:3000/rooms/11 using the HTTP verb POST. The parameters were encoded on the body of the request.
A good place to learn more about this would be the Rails routing guide.

Here is the result from rake routes command. As you can see, GET, PATCH and PUT share the same generated URL (/rooms/:id, in your example /rooms/11). Since Rails 4.0, PATCH is the default verb to update action. Update action is triggered when you're sending a form to a route.

Related

Set params hash value using link_to without affecting url in Rails 4

When I submit a form, a number of parameters are set without showing up in the url.
I would like to do the same thing with link_to:
<%= link_to((purchase.paid ? 'yes' : 'no'), {action: :index, hidden_id: purchase.id}) %>
produces the url 'http://localhost:3000/purchases?hidden_id=1'. I would like to link to the url 'http://localhost:3000/purchases' while still setting params[:hidden_id] so I can access it in the controller, as if I had submitted a form.
My routes.rb file is as follows:
root to: 'products#index'
resources :products
resources :purchases
match ':controller/(:action/(:id))', controller: :shop, via: [:get,:post]
In answering this, is there anything I should know here about the difference in the way these two things are handled? Is it something about get vs post requests or is there some other principle involved which I'm not grasping?
Yes, it's to do with Get vs Post requests.
A Get request can only send parameters in the URL itself. A post request can also be sent to a URL that includes parameters in the URL itself, but it can also send parameters 'under the hood' so to speak.
So if your routes were set up to allow it, you could send either a get or a post request to http://localhost:3000/purchases?hidden_id=1, but only the post request could include additional parameters under the hood.
Anything else you should know about the difference in the way these two are handled? Yes. In most web frameworks, when you see the parameters server-side, they will be split up into GET params and POST params. Rails doesn't make this distinction, and puts them both in the same params hash. (I think this is silly, but whatever).
Also, a get request can be sent simply by entering the URL in your browser and hitting enter. A post request will generally only be executed by a user submitting a form on a web page. For this reason, get requests are not meant to change any content in your database. They should be for viewing information only. So, eg, if you have a button to delete a resource (eg. a blog post or something) it should be submitted via post. (more info on that at Why shouldn't data be modified on an HTTP GET request?)
Lastly, Rails provides an option in it's link_to helper to allow you to easily make the 'link' use a post request. See the method option at http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to. This basically uses javascript to prevent the normal action of clicking the link (which would be a get request), and submit a post request instead.

Rails truncating of parameters with '.' in urls

I am trying to send an IP address as a parameter to a Rails method destroy in a url. There is a problem if the parameter includes .; I get not found errors, this is the log generated:
Started DELETE "/admin/user/stefan-admin/whitelist/4.3.2.1" for 127.0.0.1 at 2013-07-17 09:31:18 +0100
Processing by ErrorsController#error_404 as
Parameters: {"not_found"=>"admin/user/stefan-admin/whitelist/4.3.2"}
WARNING: Can't verify CSRF token authenticity
Session: {:user=>"admin", :role=>:admin, :user_id=>"stefan-admin"}
Completed 404 Not Found in 30ms (Views: 1.1ms | ActiveRecord: 0.0ms)
The not found message has a truncated ip address. If I use a parameter without ., e.g. abc, I don't get the not found error, and the destroy method is called.
Rails received the url, but then mangled it internally, possibly because it is processing . as an extension. Is there some way to turn off this behaviour or escape the url to avoid it?
You need to add a constraint to the routes to allow dots in params
resources :whitelists, :constraints => { :id => /[0-9.]+/ }
Or something of that kind in your routes.rb, it depends on how you write your routes but the constraints part stay the same
The reason for the "truncated" ip address is the (optional) (.:format) that resources, 'get', 'match' etc. generate on every route.
You can specify a dedicate route without the format like:
match '/admin/user/:id/whitelist/*ip', to: 'controller#action', format: false
pay attention to the * at the last parameter. It collects the whole rest of the url.
At first glance the problem could be resolved by manipulating the parameters or routing. By thinking a bit more, you will notice two more problems in your scheme:
You used GET request to process a delete action
The dots in url, if not for name extension, is very ugly.
So, instead of solving the problem directly, I suggest you to:
Review the view code containing the request link. It's better to be a button_to, or a link_to with delete method and UJS support.
By this you'll send a POST request to server without ip in URL. And you don't need any change in controller code.
Found a hacky way to do it. If your route looks like this
match 'controller/:some_param' => 'controller#action'
Then in the controller action, you can do something like this
actual_param = params[:some_param]
if params[:format]
actual_param << "." << params[:format]
end
Then use actual_param to identify the correct resource

"Params" not working to handle JSON parameters in Rails

I have a webservice application, which in one of my controllers there's an action that receives a POST from an external application.
Here's the log from my heroku app, which confirms that it's coming a Json data parameter:
2012-01-05T18:26:07+00:00 app[web.1]: Started POST "/let" for 192.168.1.145 at 2012-01-05 10:26:07 -0800
2012-01-05T18:26:07+00:00 app[web.1]: Processing by LetController#create as HTML
2012-01-05T18:26:07+00:00 app[web.1]: Parameters: {"Id"=>"dfe510c1-56e2-48ae-af0d-c4b265798f2e", "FormId"=>"5025", "Answers"=>[{"IdQuestion"=>"cfe0"
, "Value"=>"One", "FilledDate"=>"/Date(1325795160323-0200)/"}], "MSISDN"=>"8984223722"}
So, how can I access this json parameter on my method? I've tried lots of things like params[:formid], params["formid"], ActiveSupport::JSON.decode(request.body) etc etc.. and when pass to view, nothing is rendered, it's like it doesn't get anything.
It looks as though you should be able to access your params as follows:
params[:Id]
params[:FormId]
params[:Answers]
params[:Value]
params[:FilledDate]
params[:MSISDN]

protect_from_forgery does not protect PUT/DELETE requests

I made a demo application with rails new demo and then generated a scaffolded user controller with rails generate scaffold User name:string email:string. The scaffolded code has an ApplicationController with protect_from_forgery, so does UserController which derives from ApplicationController.
I run webrick, add a user, cool. Authenticity token works as promised with the POST on /users.
Yet still with Rails 3.0.5 I am able to do a:
niedakh#twettek-laptop:~$ telnet 10.0.0.4 3000
PUT /users/3 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 39
user[name]=vvvvv&user[email]=shiaus.pl
And have the user 3 modified without giving a token:
Started PUT "/users/3" for 10.0.0.4 at 2011-04-02 14:51:24 +0200
Processing by UsersController#update as HTML
Parameters: {"user"=>{"name"=>"vvvvv", "email"=>"shiaus.pl\r"}, "id"=>"3"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
', "updated_at" = '2011-04-02 12:51:24.437267' WHERE "users"."id" = 3s.pl
Redirected to http://10.0.0.4:3000/users/3
Completed 302 Found in 92ms
Also I can do the same with DELETE:
DELETE /users/3 HTTP/1.1
Which gives me:
Started DELETE "/users/3" for 10.0.0.4 at 2011-04-02 15:43:30 +0200
Processing by UsersController#destroy as HTML
Parameters: {"id"=>"3"}
SQL (0.7ms) SELECT name
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
AREL (0.5ms) DELETE FROM "users" WHERE "users"."id" = 3
Redirected to http://10.0.0.4:3000/users
Completed 302 Found in 180ms
Could you explain to me why can I do those things when I never send any token alongside those requests?
Very Short Version: protect_from_forgery is designed to protect against XSRF attacks from forged HTML FORM elements. PUT and DELETE are not vulnerable to XSRF attacks because HTML forms cannot use PUT or DELETE.
An XSRF (cross site request forgery) attack is where the victim browser is tricked into submitting a forged request to the server without interaction from the user.
Longer version: The reason you are able to do this is you either:
Have no security/login required, or
Have already logged in and are making the requests from script hosted on the same domain, or
Are making the requests via Fiddler or similar, (bypassing the browser's built-in protections).
These are not the scenario protect_from_forgery is designed to protect against.
The purpose of the protect_from_forgery is to protect against XSRF attacks - Cross Site Request Forgery. This occurs when a user visiting an evil website (or a good website with added evil) is tricked into submitting a request to another website. For example you can trick a visitor into making any GET request, like this:
<img src="http://victim.com/victimPage?action=delete&id=ID12345" />
As soon as the victim visits the Evil site, his browser will automatically attempt to retrieve the image. This will obviously not retrieve an image, but meanwhile victim.com will execute the request deleting item ID12345. POST can be forged in a similar way, just create a form, and submit it to the foreign site using script, or else trick the user into clicking on it to submit.
That is where protect_from_forgery comes in: The server sends the token to the client in a hidden field with the form. If no valid token appears, the server concludes that the form which was submitted isn't a submission of a genuine form sent by the server, so the request is rejected as potentially forged.
But you knew that.
The point is that HTTP forms can only use methods GET and POST, not PUT or DELETE. This has two effects:
First, if you get a PUT or DELETE, there is nowhere to put the protect_from_forgery token. PUT or DELETE are not the result of a form submitting, so there is no way for the server to send the token to the client, therefore the client has no token to send back.
Second, since HTML forms can only use POST and GET, if the request is a PUT or DELETE the attacker cannot use a HTML form to force or trick the user into submitting the request. They can use XMLHttpRequest, but XMLHttpRequest does not allow cross-site requests (unless enabled by security settings on both sites).
This means that, provided the domain you host it on does not contain evil code itself, it is not necessary to protect PUT and DELETE from forgery. If the server does contain evil code, the attacker can make arbitrary XMLHttpRequest requests to get a valid token, and therefore easily circumvent the forgery protection anyway.
For a quick description of XSRF try here:
http://blogs.msdn.com/b/bryansul/archive/2008/08/15/rest-and-xsrf-part-one.aspx

Rails duplicating requests

After correcting some other bugs on the application I found out that one page is being rendered twice on every request.
Rails completes the request normally and after a few, simply starts another request.
At first thought it was a Firebug problem or YSlow doing other requests, but after more tests the duplication remained and I discarded those reasons.
I tried even debugging rails step by step in the request, it goes normally and after completing the first request, I get stopped in the same debugger start line again, this time for the second request.
Printed some lines and things to see clearly on log and it clearly makes 2 requests.
I also found a few wierd requests that I cannot explain also
This bit of log shows the end of the first request and right after that one, there is a wierd index request without layout and then the same request starts again to be processed:
Processing ArtistImagesController#index (for 192.168.0.11 at 2010-07-08 15:10:56) [GET]
Parameters: {"action"=>"index", "locale"=>"pt", "controller"=>"artist_images", "artist_id"=>"2-tom-welling"}
#^ Start of first request
#v end of first request
Completed in 812ms (View: 429, DB: 41) | 200 OK [http://192.168.0.20/artistas/2-tom-welling/imagens]
SQL (0.2ms) SET NAMES 'utf8'
SQL (0.2ms) SET SQL_AUTO_IS_NULL=0
# v wierd request
Processing ApplicationController#index (for 192.168.0.11 at 2010-07-08 15:10:59) [GET]
Rendering rescues/layout (not_found)
-----------------------------------------------------> html
SQL (0.2ms) SET NAMES 'utf8'
SQL (0.2ms) SET SQL_AUTO_IS_NULL=0
# v start of second request
Processing ArtistImagesController#index (for 192.168.0.11 at 2010-07-08 15:11:00) [GET]
Parameters: {"action"=>"index", "locale"=>"pt", "controller"=>"artist_images", "artist_id"=>"2-tom-welling"}
Remembering that all those requests were generated by entering the page only once :/
Searched the code for possible loops or any kind of errors but haven't found any.
Please help is very appretiated
Search your page source for empty image src attributes. For such images the browser requests the site root, which seems to be the case.
I think your page is submitting two times. can i view your page.

Resources