I'm using Ruby on Rails 7 to build a REST API that accepts a payload via HTTP PATCH that looks like this:
{
"answers": [
{"question_id": 1, "content": "My answer content ONE"},
{"question_id": 2, "content": "My answer content TWO"}
]
}
I want to validate the structure of this response in my controller using strong parameters.
I've tried a bunch of similar SO posts but none of them seem to work for me.
Here is what I've tried so far:
params = ActionController::Parameters.new(answers: [{question_id: 1, content: 'some answer'}])
params.require(answers: [:question_id, :content])
# I get: `require': param is missing or the value is empty: {:answers=>[:question_id, :content]} (ActionController::ParameterMissing)
params.require(:answers).require([:question_id, :content])
# I get: NoMethodError on the second .require
params.require(answers: [[:question_id, :content]])
# I get: param is missing or the value is empty: {:answers=>[[:question_id, :content]]}
Any pointers on what I am doing wrong would be greatly appreciated.
CodePudding user response:
I found a solution just after I posted the question.
The solution which worked is:
def answer_params
params.require(:answer).each do |answer_param|
answer_param.require(%i[question_id, answer])
end
end
This allows me to ensure that all values are provided in the correct structure and use it in my controller method like this:
def update
answer_params.each do |answer_param|
puts answer_param['question_id']
puts answer_param['answer']
end
end
CodePudding user response:
Why insist on validating the params only through ActionController::Parameters
? I have always found limitations with the params API for nested structures (to be specific more deep structures for API I have built) like you have and in such cases I always validate the data in the most basic manner. For e.g. for the data structure you have shown I would validate it in following manner:
def validate_answers_data
received_answers_arr = params[:answers]
if received_answers_arr.blank?
# return error requires answers not received
return
end
whitelisted_answers_data = []
received_answers_arr.each do |h|
question_id = h[:question_id]
answer_content = h[:content]
if question_id.present? && answer_content.present?
whitelisted_answers_data << { question_id: question_id, content: answer_content }
end
end
if whitelisted_answers_data.blank?
# return error answers data received found invalid
return
end
whitelisted_answers_data
end
Params API is definitely useful for whitelisting data but when it poses limitations for a particular data structure, then instead of trying to insist on implementing the validation through only that API, it is better to implement through pure Ruby constructs which is the option always open.
Hope you find this suggestion helpful.
CodePudding user response:
You can define the method to ensure params structure like below:
def answer_params
params.permit(answers: %i[question_id content])
end
and then use this method in your update
action like:
def update
answer_params[:answers].each do |answer_param|
puts answer_param['question_id']
puts answer_param['answer']
end
end