Rails override passed request params - ruby-on-rails

In my Rails 7 API only app I'm receiving the request with these params:
def event_params
params.permit(:event, :envelopeId, :action, :recipient)
end
Inside the controller I need to set a guard based on event_params[:action] like below:
class EventsController < BaseController
def event_callback
return unless event_params[:action] == 'envelopefinished'
(...)
end
end
But it turns out the :action is default ActionController::Parameters parameter that represents the name of the action being performed (i.e., the method being called). Is it possible to get parameter event_params[:action] which was passed inside the JSON file without changing the JSON key name to something else?

Its actually the router that overwrites params[:action] and params[:controller] but you can still access the raw parameters through the request object:
request.POST["action"]
This odd looking method (yeah its a method not a constant) is from Rack::Request::Helpers and gives the parsed parameters from the request body.

Related

How can i solve this: params is missing?

I am using this function to find users, which i am using .require only worked when i sent both or at least one parameter but if i send empty i got errors, It should not be mandatory to send parameters
def find_params
params.require(:person).permit(:name, :age)
end
if i send the name or age i will work, but if i send nothing i am getting this error
params is missing
if i send empty like this:
{} or null it should work correctly returning all the users
or should not i use this to search users?
params.require(:person).permit(:name, :age)
might i have to user like this?
params[:name] and params[:age]
i am working with reactjs
i am sending the payload liket this:
{name:"ed", age:"12", skin:"black", weight: "180lbs", height:"183"}
Rails' StrongParameters were built for a very specific use:
It provides an interface for protecting attributes from end-user assignment. This makes Action Controller parameters forbidden to be used in Active Model mass assignment until they have been explicitly enumerated.
That means when you use the params just to read from the database, then there is no need (and hardly any advantage) to use StrongParameters.
Instead, I would just use the params directly in the controller like this:
def index
#users = User
.filter_by(:name, params.dig(:person, :name))
.filter_by(:age, params.dig(:person, :age))
# ...
end
And to make this work you will need to define an filter_by scope in your app/models/user.rb:
scope :filter_by, -> (attr, value) { where(attr => value) if value.present? }
The whole point of using ActionController::Parameters#require is to cause your create/update method to bail early if the parameter you expect to be a hash isn't sent at all since there is no point in proessing the request further and this prevents a potential uncaught nil error.
If you want to allow a key to be null use #fetch instead:
params.fetch(:person, {})
.permit(:name, :age)
#fetch allows you to pass a second key which is the default value and it returns a new ActionController::Parameters instance.
But it looks like you're actually sending flat parameters which are not nested in which case you don't need fetch either:
params.permit(:name, :age)
Note that Rails by default has parameters wrapping turned on for JSON requests and both will very likely work.
You can use Find User
Routes.rb
get "search_user", to: "users#search_user"
controllers/users_controller.rb
def search_user
#users = User.search(params[:name], params[:age], params[:skin], params[:weight], params[:height]) // search name or age
// you can use byebug to check #users
end
models/user.rb
def self.search(name, age, skin, weight, height)
if name.blank? & age.blank? & skin.blank? & weight.blank? & hight.blank?
all
else
where('name LIKE ? OR age LIKE ? OR skin LIKE ? OR weight LIKE ? OR height LIKE ?', "%#{name}%", "%#{age}%", "%#{skin}%", "%#{weight}%", "%#{height}%")
end
end
=> This is my way which i used. Hope to help you.

Ruby on Rails and JSON request processing

I have ruby on rails app and my controller should process request which creates many objects. Objects data is passed from client via json using POST method.
Example of my request (log from controller):
Processing by PersonsController#save_all as JSON
Parameters: {"_json"=>[{"date"=>"9/15/2014", "name"=>"John"},
{"date"=>"9/15/2014", "name"=>"Mike"}], "person"=>{}}
So i need to save these two users but i have some issues:
How to verify strong parameters here? Only Name and Date attributes can be passed from client
How can I convert String to Date if i use Person.new(params)?
Can i somehow preprocess my json? For example i want to replace name="Mike" to name="Mike User" and only then pass it in my model
I want to enrich params of every person by adding some default parameters, for example, i want to add status="new_created" to person params
First of all I'd name the root param something like "users", then it gives a structure that is all connected to the controller name and the data being sent.
Regarding strong params. The config depends of your rails app version. <= 3.x doesn't have this included so you need to add the gem. If you're on >= 4.x then this is already part of rails.
Next in your controller you need to define a method that will do the filtering of the params you need. I should look something like:
class PeopleController < ApplicationController
def some_action
# Here you can call a service that receives people_params and takes
# care of the creation.
if PeopleService.new(people_params).perform
# some logic
else
# some logic
end
end
private
def base_people_params
params.permit(people: [:name, :date])
end
# Usually if you don't want to manipulate the params then call the method
# just #people_params
def people_params
base_people_params.merge(people: normalized_params)
end
# In case you decided to manipulate the params then create small methods
# that would that separately. This way you would be able to understand this
# logic when returning to this code in a couple of months.
def normalized_params
return [] unless params[:people]
params[:people].each_with_object([]) do |result, person|
result << {
name: normalize_name(person[:name]),
date: normalize_date(person[:date]),
}
end
end
def normalize_date(date)
Time.parse(date)
end
def normalize_name(name)
"#{name} - User"
end
end
If you see that the code starts to get to customized take into a service. It will help to help to keep you controller thin (and healthy).
When you create one reason at the time (and not a batch like here) the code is a bit simpler, you work with hashes instead of arrays... but it's all pretty much the same.
EDIT:
If you don't need to manipulate a specific param then just don't
def normalized_params
return [] unless params[:people]
params[:people].each_with_object([]) do |result, person|
result << {
name: person[:name],
date: normalize_date(person[:date]),
}
end
end

Why does Rails use the params variable rather than passing arguments through the function?

As title says, why does Rails prefer to use the #params variable inside of a Controller action when you are responding to the action instead of passing the individual parameters through the function arguments when we call the function?
Other frameworks use this (i.e, ASP MVC) and I was just wondering if there was a reason for that design decision, because it doesn't seem very intuitive.
Ie. Why does Rails do
def index
name = params[:name]
end
Instead of
def index(name)
end
The point is, most of the actions in a controller handles the view REST-fully. The params comes from the user's browser when they interact with the page or send a new variable request to the page.
These requests are variable, and Rails makes it uniform by maintaining the parameters in params hash. If the following GET requests arrive:
http://localhost:3000/products?color=red&quality=best
the params hash will automatically be populated as {'color' => 'red', 'quality' => 'best'}. Rails doesn't expect your action to manually handle the parameters.
Similarly, consider you are getting a POST request from a page where a user filled a form. In that scenario, the params obtain the parameters which are composed with form helpers inside views.
Though in hyptothetical case you are dealing with general methods instead of actions, such as below, you will have to do it by passing arguments.
def show
if params['color'] == 'red'
#product = obtain_product('red')
else
#,..
end
end
def obtain_product(color)
Product.where('color = ?', color).first
end
Hope it is clear. :)
#kidorrails has a great answer, and I wanted to add to it:
If you wanted to pass the params to each method directly, it would go against the #1 Rails convention - keep it DRY. By having a separate params hash, you not only have access to all the params you want, but you can access them through as many methods as you need
For example, take strong_params:
#controller
def new
#model = Model.new
end
def create
#model = Model.new(strong_params)
#model.save
end
private
def strong_params
params.require(:model).permit(:your, :params)
end
As #apneadiving mentioned, the params hash is created in another part of the stack, meaning it's available over all the methods required. It's most efficient & versatile way to do it IMO

Getting the rails 'params' without the defaults?

Is there a neat way in rails to get a hash of the params without the default ones of 'action' and 'controller'? Essentially without any param that wasn't added by me.
I've settled for:
parm = params.clone
parm.delete('action')
parm.delete('controller');
But wondering if there is a neater way to do this?
You could use except:
params.except(:action, :controller)
http://as.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Hash/Except.html
request.path_parameters
returns path_parameters
request.query_parameters
returns request_parameters
You are looking for the latter.
If you are working in a controller, you should also have access to the request object.
To make a long story short, rails and rack groom incoming GET/POST requests (form, xml, json) and pull out the parameters so that developers have a consistent way of accessing them.
ActionDispatch exposes the consolidated list of params via:
# ActionPack 3.1.8 - action_dispatch/http/parameters.rb
# Returns both GET and POST \parameters in a single hash.
def parameters
#env["action_dispatch.request.parameters"] ||= begin
params = request_parameters.merge(query_parameters)
params.merge!(path_parameters)
encode_params(params).with_indifferent_access
end
end
alias :params :parameters
As you can see, params is an alias for the parameters method which is a merged hash of two sub-hashes: request_parameters and path_parameters.
In your case, you don't want the path_parameters. Rather than using except, which forces you to know which path parameters you want to exclude, you can access your data via: request.request_parameters.
A word of caution: You may be better off using :except if you require the hash to be encoded and keys to be accessed as either strings or symbols. The last line of the parameters method handles that for you:
encode_params(params).with_indifferent_access
An alternative approach using except and ensuring that you are removing all rails non-request parameters:
path_params = request.path_parameters
params.except(*path_params.keys)
use
request.request_parameters
it excludes the path_parameters (controller and action)
I use
request.request_parameters.except(controller_name.singularize)
This strips out the nested object that is named after the active controller. For example with the following controller:
Class SessionController > ActionController::Base
def create
User.find_by(params[:email]).login(password: params[:password])
puts request.request_parameters
end
end
With the following posted value from a web form:
{email: 'test#example.com', password: 'password123'}
The console output will be:
{"email"=>"test#example.com", "password"=>"password123", "session"=>{"email"=>"test#example.com", "password"=>"password123"}}
The above lines of code avoid this.

How to not use Rails action parameter in controller

I am implementing a third party API for Shipworks in a Rails server and the Shipworks client app is posting an action param with Shipworks specific semantics.
However the Rails routing logic overwrites this param to be the name of the controller method.
Is there a custom route I could write to get the value of that action param without it being overwritten to be the name of my controller method?
I fell into this trap today and came with this solution in controller method. It's Rails 4.1:
if request.method == "POST"
action = request.request_parameters['action']
else
action = request.query_parameters['action']
end
I figured it out. There is a raw_post method in AbstractRequest.
So you can do this to parse the raw post params:
def raw_post_to_hash
request.raw_post.split(/&/).inject({}) do |hash, setting|
key, val = setting.split(/=/)
hash[key.to_sym] = val
hash
end
end
and then just call raw_post_to_hash[:action] to access the original action param or any other param. There is probably an easier way.

Resources