Accept JSON in Rails Controller when wrong format - ruby-on-rails

I have the following in my Registrations Controller:
class Api::V1::RegistrationsController < ApplicationController
skip_before_filter :verify_authenticity_token
respond_to :json
def create
user = User.new(params[user_params])
if user.save
render :json => user.as_json(:auth_token=>user.authentication_token, :email=>user.email), :status=>201
return
else
warden.custom_failure!
render :json => user.errors, :status=>422
end
end
def user_params
params.require(:user).permit(:email, :password, :name, :phone, :acknowledgement)
end
end
I realize this expects my JSON to be in the form of
{user:{email:user#example.com,name:"anotheruser"}}
However the JSON is being sent as
{email:user#example.com, name:"anotheruser"}
I don't know how to target those params. What is the syntax for that?
Also, is there a special way to handle that format?

The expected json you got would be a hash of hashes, but you are only getting back a hash, is what it sounds like.
Given this, you don't need to require :user since :user isn't there. Just do this.
params.permit(:email, :password, :name, :phone, :acknowledgement) if params.present?
present? will check to ensure params is not nil or blank/empty. You could raise an error, if you wanted, if the present? check fails.

Related

Rails Adding Attributes to JSON Serializer

I had a model that should be rendered as JSON, for that I used a serializer
class UserSerializer
def initialize(user)
#user=user
end
def to_serialized_json
options ={
only: [:username, :id]
}
#user.to_json(options)
end
end
when I render json: I want though to add a JWT token and an :errors. Unfortunately I am having an hard time to understand how to add attributes to the serializer above. The following code doesn't work:
def create
#user = User.create(params.permit(:username, :password))
#token = encode_token(user_id: #user.id) if #user
render json: UserSerializer.new(#user).to_serialized_json, token: #token, errors: #user.errors.messages
end
this code only renders => "{\"id\":null,\"username\":\"\"}", how can I add the attributes token: and errors: so to render something like this but still using the serializer:
{\"id\":\"1\",\"username\":\"name\", \"token\":\"eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxfQ.7NrXg388OF4nBKLWgg2tdQHsr3HaIeZoXYPisTTk-48\", \"errors\":{}}
I coudl use
render json: {username: #user.username, id: #user.id, token: #token, errors: #user.errors.messages}
but how to obtain teh same by using the serializer?
class UserSerializer
def initialize(user)
#user=user
end
def to_serialized_json(*additional_fields)
options ={
only: [:username, :id, *additional_fields]
}
#user.to_json(options)
end
end
each time you want to add new more fields to be serialized, you can do something like UserSerializer.new(#user).to_serialized_json(:token, :errors)
if left empty, it will use the default field :id, :username
if you want the json added to be customizable
class UserSerializer
def initialize(user)
#user=user
end
def to_serialized_json(**additional_hash)
options ={
only: [:username, :id]
}
#user.as_json(options).merge(additional_hash)
end
end
UserSerializer.new(#user).to_serialized_json(token: #token, errors: #user.error.messages)
if left empty, it will still behaves like the original class you posted
Change to_json to as_json, and merge new key-value.
class UserSerializer
def initialize(user, token)
#user=user
#token=token
end
def to_serialized_json
options ={
only: [:username, :id]
}
#user.as_json(options).merge(token: #token, error: #user.errors.messages)
end
end
i prefer to use some serialization gem to handle the serialize process like
jsonapi-serializer
https://github.com/jsonapi-serializer/jsonapi-serializer
or etc

Rails default parems for id set to new?

I am getting a 404 error on my RoR app and i found that it was because one of the method in the controller, which should only triggers when the record is not new, triggered when the record is new.
I do by that checking if the id of that record nil in my controller.
before_action :create_record, if: proc { not params[:id].nil? }
I was confused by it was triggered so i went head and checked my front-end network, which show following:
Request
Parameters:
{"format"=>"json", "id"=>"new"} <----Set to new by default
My completely controller looks like this:
class Api::MyController < ApplicationController
before_action :create_recotrd, if: proc { not params[:id].nil? }
def show
end
def index
#my_model = MyModel.all
end
def create
#my_model = MyModel.new(my_model_params)
respond_to do |format|
if #my_model.save
format.json { render json: #my_model, status: :created}
else
format.json { render json: #my_model.errors, status: :unprocessable_entity}
end
end
end
def update
#my_model = MyModel.update
end
private
def create_record
#my_model = MyModel.find(params[:id])
end
def my_model_params
params.require(:my_model).permit(
:city,
:state,
:county,
:zip,
:telephone,
:email,
)
end
end
I cant seem to find out why the id in the parameters is set to "new" instead of "nil".
I tried in the console by doing MyModel.new, the default id was nil, but then when i do the GET request, the id was set to "new"
This is a really weird approach to set a new record. I think the problem lies in your routes. You are probably trying to access yoursite.com/your_model/new and your routes are configured to look for
get "your_model/:id" => "your_controller#show"
You are probably missing
get "your_model/new" => "your_controller#new"
So when you try to visit your_model/new the routes map the "new" as the :id param in your url.
I don't see a new action in your controller as well. You should read up on basic resource set up for rails here: http://guides.rubyonrails.org/routing.html.

uninitialized constant UController::User_param

I am making a basic account setup and to try to learn how the database stuff works. I have been running into this error constantly, and I have no idea how to make it disappear. I have my stuff named as U, so the URL will be easier to type a username like Reddit has it example.com/u/username
The Error is uninitialized constant UController::User_param
It highlights this code: #user = U.new(User_param)
Controller:
class UController < ApplicationController
def index
#users = U.all
end
def show
end
def create
#user = U.new(User_param)
if #user.save
redirect_to :action => 'list'
else
#user = U.all
render :action => 'new'
end
end
def User_param
params.require(:Us).permit(:id, :email, :password, :created_at, :updated_at)
end
def new
#user = U.new
end
def edit
end
end
Routes:
resources :u
U Model:
class U < ActiveRecord::Base
end
In Rails you don't capitalize methods, only constants and classes. change User_param to user_params along with the method and that should work. I made params plural since it is clearer and easier to understand
Also, change the user_param method to this:
def user_params
params.require(:u).permit(:id, :email, :password, :created_at, :updated_at)
end
The .require(:u) doesn't need to be plural as you had it.

How to parse a parameter in a URI for use in a controller?

I want to be able to use the ID present in the route
post 'users/:id/contacts/create' => 'contacts#create'. I am trying to parse the ID by params[:id]. As shown below, in my contact controller:
def create
#contact = Contact.new(contact_params)
if #contact.save
render json: #contact, status: :created, location: #contact
else
render json: #contact.errors, status: :unprocessable_entity
end
end
The contact params function is:
def contact_params
params.require(:contact).permit( :name, :email, :phone, :user_id=>params[:id])
end
However, when I am posting the data, the :user_id is returning as null.
Please try this:
def contact_params
params[:contact][:user_id] = params[:id]
params.require(:contact).permit( :name, :email, :phone, :user_id )
end
Maybe you want to change the id parameter route to nested because it is not the primary object/resource of the ContactsController
resources :users do
resources :contacts, as: 'users_contacts'
end
Then run bundle exec rake routes CONTROLLER=users
Should print how its setup.
I think your parameters should then appear as you want them
params.require(:contact).permit( :name, :email, :phone, :user_id)
Remember you also want to do this so as to follow the rails way. Any deviation from standard way will make things complex unnecessarily in future.
As a suggestion try generating a default scaffold controller with routes done like that.
rails g scaffold_controller Contacts
That should have this object saved via post in the code and a sneak peak at the parameters.
Hope it helps.

Rails 4.2: using deliver_later with a tableless model

I am trying to setup a contact form using Rails 4.2's deliver_later method. However, I can only get deliver_now to work, as deliver_later is trying to serialize my object and fails each time.
Here's my setup:
messages_controller.rb
class MessagesController < ApplicationController
def new
#message = Message.new
end
def create
#message = Message.new(params[:message])
if #message.valid?
ContactMailer.contact_form(#message).deliver_later
redirect_to root_path, notice: "Message sent! Thank you for contacting us."
else
render :new
end
end
end
contact_mailer.rb
class ContactMailer < ApplicationMailer
default :to => Rails.application.secrets['email']
def contact_form(msg)
#message = msg
mail(:subject => msg.subject, from: msg.email)
end
end
message.rb
class Message
include ActiveModel::Model
include ActiveModel::Conversion
## Not sure if this is needed ##
include ActiveModel::Serialization
extend ActiveModel::Naming
attr_accessor :name, :subject, :email, :body
validates_presence_of :email, :body
validates_format_of :email, with: /\A([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})\z/i
validates_length_of :body, :maximum => 1000
def initialize(attributes = {})
attributes.each { |name, value| send("#{name}=", value) }
end
## Not sure if this is needed ##
def attribtues
{'name' => nil, 'subject' => nil, 'email' => nil, 'body' => nil}
end
end
The error I get when calling ContactMailer.contact_form(#message).deliver_later is:
ActiveJob::SerializationError in MessagesController#create
Unsupported argument type: Message
Extracted source (around line #10):
if #message.valid?
ContactMailer.contact_form(#message).deliver_later
redirect_to root_path, notice: "Message sent! Thank you for contacting us."
else
render :new
Ideally I'd like this to be a background process. I will be adding something like Sidekiq soon but I think it's best I get this serialization problem fixed beforehand.
Any help is appreciated! Thanks :)
In order to use your class with ActiveJob (that's what deliver_later delegates to), it needs to be able to uniquely identify the object by its ID. Further, it needs to find it later by the ID when deserializing (no manual deserialize is necessary in the mailer / job).
class Message
...
include GlobalID::Identification
...
def id
...
end
def self.find(id)
...
end
end
ActiveRecord would provide you with these methods but since you're not using it, you need to implement it yourself. It's up to you to decide where you want to store the record but honestly I think you'd be better off by using ActiveRecord and the table underneath.
A simple solution that avoids having to back the object with ActiveRecord or create an unnecessary table:
Instead of passing the Message object to the contact_form method, you can also pass the message params to the contact_form method and then initialize the Message object inside that method.
This will solve the problem without having to create a table, because you are initializing the object in the delayed job worker's memory space.
For example:
messages_controller.rb
MessagesController < ApplicationController
def new
#message = Message.new
end
def create
#message = Message.new(params[:message])
if #message.valid?
ContactMailer.contact_form(params[:message]).deliver_later
redirect_to root_path, notice: "Message sent! Thank you for contacting us."
else
render :new
end
end
end
contact_mailer.rb
class ContactMailer < ApplicationMailer
default :to => Rails.application.secrets['email']
def contact_form(msg_params)
#message = Message.new(msg_params)
mail(:subject => msg.subject, from: msg.email)
end
end
I had a similar problem today and solved it as follows.
Convert a tableless object into a JSON sting
Pass it to a mailer
Convert the json string to hash
Environment
Rails 5.0.2
messages_controller.rb
class MessagesController < ApplicationController
# ...
def create
#message = Message.new(message_params)
if #message.valid?
ContactMailer.contact_form(#message.serialize).deliver_later
redirect_to root_path, notice: "Message sent! Thank you for contacting us."
else
render :new
end
end
# ...
end
contact_mailer.rb
class ContactMailer < ApplicationMailer
default :to => Rails.application.secrets['email']
def contact_form(message_json)
#message = JSON.parse(message_json).with_indifferent_access
mail(subject: #message[:subject], from: #message[:email])
end
end
message.rb
class Message
include ActiveModel::Model
attr_accessor :name, :subject, :email, :body
validates_presence_of :email, :body
validates_format_of :email, with: /\A([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})\z/i
validates_length_of :body, :maximum => 1000
# Convert an object to a JSON string
def serialize
ActiveSupport::JSON.encode(self.as_json)
end
end
Hope this will help anybody.
You'll need to serialize the object before passing to AJ and deserialize in the mailer.

Resources