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 !
Related
I have a form in RoR with a controller action that looks up a record via the get parameter.
def respond
if request.post?
# Submit logic here...
# cannot lookup this way to fill the form out again
# #current_message = Saved_message.find_by_id(params[:msg_id])
elsif request.get?
#current_message = Saved_message.find_by_id(params[:msg_id])
end
end
I can't use the params[:msg_id] to lookup the message again because it's a post request and I don't resend the get parameters. However, the get parameters remain in the url such as .../messages/respond?msg_id=2. I can get around this by passing in a hidden field with a different parameter name like <%= form.hidden_field :msg_id_2, value: params[:msg_id] %>. Then I can lookup the #current_message via the params[:msg_id_2]. However, I don't like this solution. Any advice to access the now inaccessible get parameter?
you should use RESTful routes so that you do not have to care about such issues.
since you are not posting much about the actual code or problem you are trying to solve, i can just assume what might be the issue here and how to solve it.
Update
This was a legacy app I inherited, and I found out that the previous developers had removed the rack code that converted browser POST requests into PUT/PATCH based on the _method param that Rails adds to your forms.
# config/application.rb
# This is the line that caused the problem...
config.middleware.delete ::Rack::MethodOverride
Once I removed that line and restarted the server, things worked as expected.
Original Question
When I post a Rails form using the standard resources in the routes file, it raises a route not found error when I'm trying to update an existing record:
No route matches [POST] "/admin/lookups/record_types/1"
The model is namespaced as app/models/lookups/record_type.rb
# model file
module Lookups
class RecordType < ApplicationRecord
# ...
end
end
# form in view file
<%= form_with model: #record_type, scope: :record_type, url: [:admin, #record_type], local: true do |form| %>
<%= form.text_field :value %>
<% end %>
# Request being sent
POST admin/lookups/record_types/1
{ record_type: { _method: "patch", value: "value" } }
# in routes .rb
namespace :admin do
namespace :lookups do
# Does not work
resources :record_types
# Works when explicitly written out
post "record_types/:id", controller: record_types, action: :update
end
end
When I explicitly write out the POST request in the routes.rb file, it works as expected.
I know that Rails is actually POSTing the request and using the _method hidden attribute to map the routes file. However, something isn't converting that request properly.
It's an application I inherited, and at one point it was exclusively an JSON API (no direct UI), so I'm wondering if there was something removed that converted the Rails _method param to the proper controller? I don't know what that would be, though.
This is the output of my rake routes:
admin_lookups_record_types
GET /admin/lookups/record_types(.:format)
admin/lookups/record_types#index
POST /admin/lookups/record_types(.:format)
admin/lookups/record_types#create
new_admin_lookups_record_type
GET /admin/lookups/record_types/new(.:format)
admin/lookups/record_types#new
edit_admin_lookups_record_type
GET /admin/lookups/record_types/:id/edit(.:format)
admin/lookups/record_types#edit
admin_lookups_record_type
GET /admin/lookups/record_types/:id(.:format)
admin/lookups/record_types#show
PATCH /admin/lookups/record_types/:id(.:format)
admin/lookups/record_types#update
PUT /admin/lookups/record_types/:id(.:format)
admin/lookups/record_types#update
DELETE /admin/lookups/record_types/:id(.:format)
admin/lookups/record_types#destroy
The problem seems to be the use of the scope argument to the form_with method.
If you take a look at the routes you'll note that the route to the update action uses PUT or PATCH whereas the route to the create action uses POST.
As the documentation of the FormHelper module states:
in the options hash. If the verb is not GET or POST, which are natively supported by HTML forms, the form will be set to POST and a hidden input called _method will carry the intended verb for the server to interpret.
But nesting the special _method key inside record_type breaks this mechanism. Is the scope really necessary? I'd try removing it, it should work fine without it. The correct HTTP verb to use would be PUT or PATCH. Adding an additional POST route breaks the regular Rails structure without any real gain.
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.
I am just starting to wrap my head around parameters in rails. I am currently working on a project that isn't accessible to the public, so keeping params secure isn't exactly a priority in this case.
I have a link_to to a different controller action that requires an object id to fulfil the controller action.
=link_to "Barcode", print_barcode_label_admin_items_path(:item_to_print => { :article_id => article.id })
Then in the relevant controller
def print_barcode_label
if params[:item_to_print][:article_id].present?
return if force_format :pdf
..........
private
def params_document
params.require(:document).permit!
end
As I was writing the code for this controller I am certain the parameters were being passed (I am using the better-errors gem to debug along the way so I could see them being passed in the request parameters hash). But now, not sure what I have done, but i get the error
undefined method `[]' for nil:NilClass
failing at line two in my above controller action. I am sure there is something really basic I am missing. What is it? Is there a more favourable way of doing this?
Update
So I started playing with other possible solutions, and one is naming a route that specifically carries the parameter
get 'print_barcode_label/:article_id', to: 'documents#print_barcode_label', as: 'print_barcode_label'
This seems a more robust and sensible approach. Howeever, despite passing the variable in the link, like this
=link_to "Barcode", print_barcode_label_admin_items_path(article.id)
Gives a no route matches error
No route matches {:action=>"print_barcode_label", :controller=>"admin/documents"} missing required keys: [:article_id]
It is hard to answer this question without seeing more code with some context. But if you want to do rails way you should propably create custom action on document resource.
In your routes.rb:
namespace :admin do
resources :documents
get :print_barcode_label, :on => :member
end
end
And then you can create link to this action:
= link_to 'Barcode', print_barcode_label_admin_document_path(article)
I am using the twitter gem for ruby and need to send a POST request to users/lookup endpoint.
As per the gem source code documentation(https://github.com/sferik/twitter/blob/4e8c6dce258073c4ba64f7abdcf604570043af71/lib/twitter/rest/users.rb), the request should be POST by default, unless I pass :get :
#option options [Symbol, String] :method Requests users via a GET request instead of the standard POST request if set to ':get'.
def users(*args)
arguments = Twitter::Arguments.new(args)
request_method = arguments.options.delete(:method) || :post
flat_pmap(arguments.each_slice(MAX_USERS_PER_REQUEST)) do |users|
perform_with_objects(request_method, '/1.1/users/lookup.json', merge_users(arguments.options, users), Twitter::User)
end
end
I am calling it as follows:
users = #client.users(twitter_screen_names_arr, [:method, :post])
However, I am not sure if this is actually resulting in a POST request / a GET request.
How can I make sure if this is a POST/GET? I would like to print the request that is being made to get a clarity on what actually gets sent.
Thanks!
As you can see from the code it uses POST by default. This behavior is also specified with RSpec.
You can invoke the users method like this:
#client.users(twitter_screen_names_arr, :method => :post)
or simply
#client.users(twitter_screen_names_arr)
since POST is the default request method.
If you don’t trust the code or the specs, you could run the request through a proxy to verify this behavior manually.