Setting Up Rails to Receive HTTP POST Request - ruby-on-rails

Is setting up Rails to receive a HTTP POST request that sends information encoded in JSON form as easy as adding the following code to the sessions_controller? Or are there other steps involved?
def create
if user = User.authenticate(params["email"], params["password"])
session[:user_id] = user.id
render:json => "{\"r\": \"t\"}" + req
else
render :json => "{\"r\": \"f\"}"
end
end

Should be that easy, though you will need to add a route to your routes.rb file as well specifying POST as the HTTP verb and pointing it to sessions#create. You also might want to use strong parameters just to validate what parameters are required and which you'll accept. As a heads up, I'm not entirely sure what "{\"r\": \"t\"}" + req is supposed to represent. It looks like req would be undefined in this case, but perhaps you're just omitting some code. Lastly, render :json => ... is sort of the old way of including a hash. I believe as of Ruby 2 the standard is something more like render json: .... Hopefully that helps.

Related

No route matches [GET] on a post request

I started Ruby on Rails few days ago, and I'm struggling with routing.
Indeed, I would like to make a post request through my routes.rb, but I keep having a
No route matches [GET] "/orders/refresh"
error.
Here is my routes.rb :
# frozen_string_literal: true
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
get '/orders', to: 'orders#index'
get '/orders/active(/:q)', to: 'orders#active'
post '/orders/refresh', to: 'orders#refresh'
end
and here is my controller (orders_controller.rb) :
# frozen_string_literal: true
class OrdersController < ApplicationController
def index
#orders = Order.order(:departure_date).all
render json: #orders.to_json
end
def active
if !params[:q]
#orders = Order.order(:departure_date).where(active: true)
else
#orders = Order.order(:departure_date).where("reference = ? OR client_name = ? OR departure_city = ? OR arrival_city = ?",
params[:q], params[:q], params[:q], params[:q])
.where(active: true)
end
render json: #orders.to_json
end
def refresh
response = RestClient.get 'https://wakeo-technical-test.s3.eu-west-3.amazonaws.com/api.json'
json = JSON.parse response
if !json.nil?
json.each do |order|
old_order = Order.find_by(reference: order["client_number"])
if !old_order.nil?
old_order.update(departure_date: order["dep_time"])
old_order.update(arrival_date: order["arr_time"])
old_order.update(client_name: order["company"])
old_order.update(departure_city: order["dep_city"])
old_order.update(arrival_city: order["arr_city"])
end
end
else
puts "error seeding external API"
end
end
end
From what I have understood, it seems like RoR will try to find a GET request for that specific URL, and since it won't find any, it will throw that error. How could I make that request be a POST for Rails ?
Also, I would appreciate any suggestion about how I should use ActiveRecord Querying, I'm pretty sure I could do it better here.
Thanks, have a great day !
EDIT : Here is the list of different routes my app seems to be capable of, including my POST.
Routes and error
The most common reason you unexpectly get GET requests instead of PUT, PATCH, POST or DELETE is that you are using link_to 'Something', '/some_path', method: :post and you broke the Rails Unobtrusive Javascript Driver (Rails UJS):
Because submitting forms with HTTP methods other than GET and POST
isn't widely supported across browsers, all other HTTP methods are
actually sent over POST with the intended method indicated in the
_method parameter. Rails automatically detects and compensates for this.
Rails does that with a JavaScript event handler attached to any link with the data-method attribute. But if you broke that functionality the browser will just perform its default action which is sending a GET request when the user clicks a link.
This problem usually boils down to one or more of:
Your javascript is throwing an error which halts script execution (use the browser console to find the error, make it suck less).
Rails UJS is not included in your assets pipeline or webpacker packs and thus not in the page.
The quick and easy solution to sidestep the problem is by using button_to which actually creates a form and does not require any JavaScript trickery. After all forms can send POST requests. And by just passing a _METHOD hidden field Rack will treat the request as any other HTTP verb.
button_to 'Something', '/some_path', method: :post
But in the long run you should probably fix the problem if you want to use any of the features of Rails UJS.
Your routes.rb is expecting a POST request to /orders/refresh routes, but apparently you are testing with a GET request.
Try changing your routes.rb:
Rails.application.routes.draw do
# ...
get '/orders/refresh', to: 'orders#refresh'
end
... or change your request to a POST request. If you are using Rails forms, you must do something like this:
form_with(url: "/orders/refresh", method: "post")
Ok, I think I figured it out.
It might be because when I hit /orders/refresh directly in my web browser, it will try to find a GET corresponding to the request.
I managed to make POST using a client like Postman, and everything works fine.
Thank you for your help !

Replace GET params in redirect_to request.referer

I'm trying to Replace GET params in:
redirect_to request.referer
My request.referer already contains one parameter:
http://www.foo.com/bar?par=10
When i try:
redirect_to request.referer, :par => 5
it doesn't work. It redirects to referer but doesn't change :par from 10 to 5.
When i do redirect to url_path, e.g.
redirect_to root_path, :par => 5
This works ok, redirects to:
http://www.foo.com/?par=5
So my question is how to replace params in request.referer URI. Additional question is whether should I use request.referer or :back ?
Thanks
The problem is that redirect_to ultimately just takes a string, ie the url. If you were to do something like
redirect_to edit_foo_path(#foo, :bar => "qux")
then you're using a path helper to generate that string. ie, edit_foo_path(:bar => "qux") is the helper and it will be converted to "/foo/123/edit?bar=qux" which is just a "dumb" string. If you were working with the helper you can switch the params around but with the string it's already finished, if you know what i mean.
request.referer is a string as well, so what you'll need to do is to break it down into its constituent parts, modify those parts as required, and then reassemble it into a string again. The parts in question are protocol, host, path & params. You don't need to change the protocol, host or path in this case so you can keep them the same. params will be most easily manipulated when converted to a hash. Rails has various url-processing functions which you can use here, so there's probably a few different ways of doing this. I would do this like follows, which probably isn't the most efficient.
url = URL(request.referer)
#you could get this via a regex but i'm doing it "formally" with the Url object
host_and_path = "#{url.scheme}://#{url.host}#{url.path}"
params = CGI.parse(url.query)
#now you've got params as a hash you can do what you want to it.
params["par"] = 5
new_url = "#{host_and_path}?#{params.to_param}"
redirect_to new_url
like i say there's probably more efficient (in terms of lines of code, there's no issues with it speed-wise) ways to do this, but it's useful to see the step-by-step approach anyway i guess.

Rails redirect_to with params

I want to pass parameters (a hash) to redirect_to, how to do this? For example:
hash = { :parm1 => "hi", :parm2 => "hi" }
and I want to redirect to page /hello
URL like this: /hello?parm1=hi&parm2=hi
If you don't have a named route for /hello then you'll have to hardcode the params into the string that you pass to redirect_to.
But if you had something like hello_path then you could use redirect_to hello_path(:param1 => 1, :param2 => 2)
Instead of:
redirect_to some_params
You can do:
redirect_to url_for(some_params)
You're turning the params into a url with url_for before passing it to redirect_to, so what you pass redirect_to ends up being a URL as a string, which redirect_to is happy to redirect to.
Note well: I don't understand why redirect_to refuses to use params. It used to be willing to use params. At some points someone added something to Rails to forbid it. It makes me suspect that there are security reasons for doing so, and if so, these security reasons could mean that manually doing redirect_to url_for(p) has security implications too. But I haven't yet been able to find any documentation explaining what's up here.
update: I've found the security warning, but haven't digested it yet: https://github.com/rails/rails/pull/16170
The easiest way (if it's not a named route) will be:
redirect_to "/hello?#{hash.to_param}"
See: http://apidock.com/rails/Hash/to_param
Simply, pass the hash into an argument in the URL, and in your code parse it to get out all needed values.
param_arr = []
hash.each do |key , val|
param_arr << "#{key}=#{val}"
end
params_str = param_arr.join("&")
redirect_to "http://somesite.com/somepage?#{params_str}"
I know this might be very basic way to do it, but hey, it'll get you somewhere :)

rails post request

I'm not quite understanding how requests are handled in rails, in my controller, I have something like this:
def method_to_handle_request
# do stuff
end
Then in the view I have this JavaScript:
$.post("/", {method_to_handle_request: "value"}, function(data) {
// do something with data
});
This is wrong: data in the JavaScript ends up just being the page. Thus, my question is: how do I handle requests in rails? I've been reading "Agile Web Development With Rails", and the section there doesn't make too much sense to me.
Thanks
Rails uses configured routes to point to the appropriate controller action. You have to specify a route in your config/routes.rb file to send your request through the desired action. In your controller, you've defined the method_to_handle_request. You have to make sure that you define a route for that. There are many ways to do this within the routes.rb file and those are well documented in this guide.
Then in your method_to_handle_request you should render/format your output as JSON or XML or whatever you want your view to consume. If you don't do this you'll end up just getting the flat templated HTML back. Here's a good resource for rendering views that has a section on JSON in particular.
So here's a quick example of what I mean:
in config/routes.rb
resources :foos do
member do
post 'do_my_foo'
end
end
in foos_controller.rb
def do_my_foo
foo = Foo.find(params[:id])
# do some things to poor ole foo
some_cool_data_about_foo = ...
respond_to do |format|
format.json { render :json => some_cool_data_about_foo }
end
end
Then when you call it with ajax you'll get the JSONified data in your handler.
Geeze, I hope this is what you were asking...

How do can you make redirect_to use a different HTTP request?

At the end of one of my controller actions I need to redirect to a page that only accepts put requests. I have been trying to figure out how to get redirect_to to use a put request but to no success.
Is this possible? Or is there another way to accomplish this?
I don't think you are able to do this, and I suspect that the limitation is part of HTTP itself.
When using redirect_to - the redirection happens as a "302 Moved" header unless otherwise specified in the parameters.
Having a look at the HTTP Spec itself doesn't reveal any way to change the type of request the browser makes via redirect.
HTTP Redirects:
This class of status code indicates
that further action needs to be taken
by the user agent in order to fulfill
the request. The action required MAY
be carried out by the user agent
without interaction with the user if
and only if the method used in the
second request is GET or HEAD.
I think you may need to use JavaScript to achieve this functionality, or perhaps rethink the flow of control in your application.
If the action is in the same controller as where you're trying to redirect from, simply call the action and render the template like so:
def show
index
render :action => "index"
end
If it's not, then I don't know how you do that.
Ok, so I found a solution to my problem. I found a very good write up on the situation here. My implementation looks like this:
private
def redirect_post(redirect_post_params)
controller_name = redirect_post_params[:controller]
controller = "#{controller_name.camelize}Controller".constantize
# Throw out existing params and merge the stored ones
request.parameters.reject! { true }
request.parameters.merge!(redirect_post_params)
controller.process(request, response)
if response.redirected_to
#performed_redirect = true
else
#performed_render = true
end
end
Then I called this method like this:
redirect_post :controller => 'registrations', :action => 'order', :_method => 'put', :authenticity_token => params[:authenticity_token]
So I was able to 'fake' a put request by making a post request (using redirect_post) and then assigning 'put' to a _method param. If you look at a normal put request all it is a post from a form with a _method param. So its a bit hackish but it gets the job done.
Also, you have to make sure that when you call redirect_post the values of your hash are strings otherwise errors will be thrown.
You could redirect to a different page that issues the put request from the client, using Javascript.

Resources