I m building my first REST API using Rails and have a simple question which I am unable to solve.
Basically i am not able to write a params hash into the database from a http PUSH request.
I have the following user controller under the Api namespace with the create action.
class Api::UserController < ApplicationController
http_basic_authenticate_with :name => "test", :password => "test"
skip_before_action :verify_authenticity_token
def create
#user = User.create(params[:user])
respond_to do |format|
if #user.save
format.json{render json: #user, status: :created, location: #user} #render create full response
else
format.json{render json: #user.errors, status: :unprocessable_entity}
end
end
end
end
The route to this action is
POST /api/user(.:format) api/user#create{:format=>"json"}
The user model has a name and a address string not contained.
To test the REST call i setup the firefox plugins RESTeasy and RESTclient
with this URL http://localhost:3000/api/user/
and this body
{
"name": "timoteo",
"address": "berlin"
}
So far so good. Now after sending this package Webrick gives me this output:
Started POST "/api/user" for ::1 at 2015-08-03 17:06:50 +0200
Processing by Api::UserController#create as JSON
Parameters: {"name"=>"timoteo", "address"=>"berlin"}
(1.2ms) BEGIN
SQL (0.6ms) INSERT INTO "users" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", "2015-08-03 15:06:50.646169"], ["updated_at", "2015-08-03 15:06:50.646169"]]
(42.8ms) COMMIT
(0.3ms) BEGIN
(0.3ms) COMMIT
Completed 201 Created in 66ms (Views: 1.4ms | ActiveRecord: 48.1ms)
neglecting the two parameter and an empty empty record like this one gets stored in the database(also the REST clients show me the same)
{"id":19,"name":null,"address":null,"created_at":"2015-08-03T15:06:50.646Z","updated_at":"2015-08-03T15:06:50.646Z"}
Can someone give me a hint what might have gone wrong during the way. I assume it is something with the params hash but i could not figure it out yet.
Add validation to your model:
class User < ActiveRecord::Base
validates_presence_of :name
validates_presence_of :address
end
That will disallow saving record with blank attributes.
Then, change your #save call to #persisted? in your controller (because record is created immediately after #create is called, it is already saved or failed)
if #user.persisted?
format.json{render json: #user, status: :created, location: #user} #render create full response
else
format.json{render json: #user.errors, status: :unprocessable_entity}
end
UPD. You could also benefit from whitelisting parameters which you accept:
#user = User.create(params.require(:name, :address))
This way Rails will respond with error, saying parameter is missing, and will not assign any other attributes to user (which you don't want to be assigned).
Your params of { "name": "timoteo", "address": "berlin" } are incorrect when your user creation is #user = User.create(params[:user])
You params should look more like:{ "user": { "name": "timoteo", "address": "berlin" } } because you're accessing params[:user] so the user attributes need to belong to the user section of your params.
Adding Validations may help if its incorrect to save a User with no name/address too.
class User < ActiveRecord::Base
validates :name, presence: true
validates :address, presence: true
# etc...
end
Related
I have a Rails model called user that has a birthday field with type date in Postgres.
My first issue is that I'm not sure how to update the value of a user. If I pass in a string like "10/10/1980", it does not update.
The second issue is that even when the birthday is not updated, Rails returns true for the User.update(user_params) action
My two questions:
How can I update a date field?
How can I make sure that rails throws an error if an incorrect date is passed in?
How can I update a date field?
Update method from controller below. Fairly standard from scaffolding.
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
puts(user_params)
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
Edit: Params:
def user_params
params.require(:user)
.permit(:points, :payment_option, :first_name, :last_name,
:payment_email, :phone_number, :facebook_id, :profile_pic_url,
:locale, :timezone, :gender, :email, :country, :city,
:age_group, :birthday, :employment_status, :occupation,
:education_level, :income_bracket)
end
Rails log (note that I added a puts(user_params) after the update statement.
Started PATCH "/api/v1/users/1" for 127.0.0.1 at 2017-12-07 19:16:37 +0800
Processing by UsersController#update as JSON
Parameters: {"user"=>{"birthday"=>"12dsafkljfsldk"}, "id"=>"1"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Can't verify CSRF token authenticity.
{"birthday"=>"10/10/1900"}
(0.2ms) BEGIN
(0.1ms) COMMIT
Rendering users/show.json.jbuilder
Rendered users/_user.json.jbuilder (0.6ms)
Rendered users/show.json.jbuilder (1.6ms)
Completed 200 OK in 31ms (Views: 5.9ms | ActiveRecord: 3.1ms)
I was hoping that I could use before_update to parse the date string into a Date object before saving, but it doesn't look like self.birthday is even showing up in the model.
Place this line in your ApplicationController
protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }
If it doesn't work then. Try to skip the action
skip_before_action :verify_authenticity_token
I'm trying to send a POST request from some client to a rails server and I'm having some problems.The full requirement is to send an image to to be processed by paperclip but it look like it's a general postman multipart POST with Rails problem.
This is what I'm getting:
Bellow my setup:
class CategoriesController < ApplicationController
def create
#category = Category.new(category_params)
respond_to do |format|
if #category.save
format.html { redirect_to #category, notice: 'Category was successfully created.' }
format.json { render :show, status: :created, location: #category }
else
format.html { render :new }
format.json { render json: #category.errors, status: :unprocessable_entity }
end
end
end
private
def category_params
params.require(:category).permit(:label, :description)
end
I'm assuming the problem is that the Request params are not encapsulated int the "categories".
Please let me know if I wasn't clear enough and if I can offer more info.
Thanks in advance.
EDIT:
As suggested by fylooi I've changed the Request Body in Postman adding an encapsulating "entity" like this:
Still I'm getting the same results
Processing by CategoriesController#create as JSON
Parameters: {"------WebKitFormBoundaryFdJXZFMuAl0fZf3Q\r\nContent-Disposition: form-data; name"=>"\"category[label]\"\r\n\r\nTraffic\r\n------WebKitFormBoundaryFdJXZFMuAl0fZf3Q\r\nContent-Disposition: form-data; name=\"category[description]\"\r\n\r\nTraffic category\r\n------WebKitFormBoundaryFdJXZFMuAl0fZf3Q--\r\n"}
Completed 400 Bad Request in 1ms (ActiveRecord: 0.0ms)
ActionController::ParameterMissing (param is missing or the value is empty: category):
app/controllers/categories_controller.rb:67:in `category_params'
app/controllers/categories_controller.rb:27:in `create'
Postman works fine with Rails, you just need to understand how Rails handles parameters in general.
Let's say you POST the following parameters to the server:
plain_param=value
nested_object[attribute]=value
This gets parsed into the following:
pry(main)> params = ActionController::Parameters.new(plain_param:"value", nested_object: { attribute: "value" } )
=> {"plain_param"=>"value", "nested_object"=>{"attribute"=>"value"}}
Let's take a look at how permit works.
params.permit(:plain_param)
pry(main)> params.permit(:plain_param)
Unpermitted parameter: nested_object
=> {"plain_param"=>"value"}
pry(main)> params.permit(:nested_object)
Unpermitted parameters: plain_param, nested_object
=> {}
pry(main)> params.permit(:nested_object => :attribute)
Unpermitted parameter: plain_param
=> {"nested_object"=>{"attribute"=>"value"}}
pry(main)> params.permit(:plain_param, :nested_object => :attribute )
=> {"plain_param"=>"value", "nested_object"=>{"attribute"=>"value"}}
So far, so good. Looks like permit returns the entire hash for top level and nested permitted keys through and prints an alert for unpermitted keys. How about require?
[33] pry(main)> params
=> {"plain_param"=>"value", "nested_object"=>{"attribute"=>"value"}}
pry(main)> params.require(:plain_param)
=> "value"
pry(main)> params.require(:nested_object)
=> {"attribute"=>"value"}
pry(main)> params.require(:nested_object => :attribute)
ActionController::ParameterMissing: param is missing or the value is empty: {:nested_object=>:attribute}
pry(main)> params.require(:plain_param, :nested_object)
ArgumentError: wrong number of arguments (2 for 1)
We can see that require returns the value for a single param key. This comes in handy to ensure the presence of objects with multiple attributes.
Wrapping up:
params.require(:category).permit(:label, :description)
expects a hash of the form
{:category=>{:label=>"value", :description=>"value"}}
which translates to HTML POST parameters of
category[label]=value
category[description]=value
Edit: Postman automagically sets the content-type header for multi part file upload, so do not set it manually. Not sure whether this is considered a bug or a feature.
https://github.com/postmanlabs/postman-app-support/issues/191
Hi everyone. I am using Rails 4, and i am working on api. Below are my code.
**Api::DocumentsController**
class Api::DocumentsController < Api::BaseController
skip_before_filter :verify_authenticity_token
respond_to :json, :xml
def create
#document = Document.new(document_params.merge(user_id: current_user.id))
#signers = current_user.signers
#signer_ids = params[:signer_ids]
if #document.save
#document.create_activity action: :create, recipient: current_user, owner: current_user, custom_field: request.remote_ip
if #signer_ids.present?
#signer_ids.each do |id|
unless id.blank?
DocumentSigner.find_or_create_by(document_id: #document.id, signer_id: id)
end
end
end
#response_message = {:message => "Your document has been saved.", :document => #document}
else
#response_message = {:message => "Document creation failed. Please try again!"}
end
respond_to do |format|
format.json {
render json: #response_message
}
end
end
private
def document_params
params.require(:document).permit(:document_name, :document, :email_body, :user_id, {:signers_attributes => [:first_name, :last_name, :email, :is_clicked, :document_id, :user_id, :_destroy]})
end
end
When i save document without api calls, i have following output.
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0gVL8K5nfsQfy/AmBqDQpzTF4aHs7uASSof4b11r4ME=", "document"=>{"document"=>#<ActionDispatch::Http::UploadedFile:0x00000006f63398 #tempfile=#<Tempfile:/tmp/RackMultipart20150214-11121-1004vyd>, #original_filename="mozilla.pdf", #content_type="application/pdf", #headers="Content-Disposition: form-data; name=\"document[document]\"; filename=\"mozilla.pdf\"\r\nContent-Type: application/pdf\r\n">, "document_name"=>"my document", "signers_attributes"=>{"0"=>{"user_id"=>"1", "is_clicked"=>"true", "_destroy"=>"false"}, "1423910618865"=>{"user_id"=>"1", "is_clicked"=>"true", "_destroy"=>"false"}}, "email_body"=>"this is the message value"}, "signer_ids"=>["1", "3"], "commit"=>"Open Document", "payment"=>{"is_payment_with_signature"=>"false", "signer_id"=>"", "payment_amount"=>""}}
Document model with pdf upload feature. Document will have many signers and accepts nested attributes for signers.
And signers_attributes which is a nested attributes in document which will save user_id, is_clicked, email_body, etc.
Now i am not able to access this create action through curl command for nested attributes. Can anyone please help me in creating curl command for my create action including signers nested attributes and pdf upload.
curl -X POST -F document=/home/rails/Documents/mozilla.pdf -F 'document[:name]=document nameeee &api_key=-pq45wMEMgjtP5v5GUw3' http://localhost:3000/api/documents.json
I've been trying to write to a regular one model rails app from POSTman for a few days now, and can't figure out how to do it, or find any information on how to do it.
My app has a User model with a name field. All I'm trying to do is change the name remotely via JSON using POSTman.
Any help on how to do this is appreciated, including a link to a basic resource on how to do it.
I imagine this is pretty basic.
EDIT: here is a screenshot from the POSTman output
EDIT 2:
from server log:
Started PUT "/users/1.json" for 127.0.0.1 at 2013-07-17 16:53:36 -0400
Processing by UsersController#update as JSON
Parameters: {"name"=>"Jeff", "id"=>"1"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", "1"]]
(0.1ms) begin transaction
(0.1ms) commit transaction
Completed 204 No Content in 3ms (ActiveRecord: 0.4ms)
The transaction is happening, but the record isn't actually being updated. Do I need to change something in the controller? It's just a regular rails generated controller with no changes.
EDIT 3:
Here is my output from just going to http://localhost:3000/users/1.json
{
"created_at": "2013-07-02T21:51:22Z",
"id": 1,
"name": "Arel",
"updated_at": "2013-07-02T21:51:22Z"
}
Again, I've changed nothing from the scaffold, and I haven't been able to figure out how to format the JSON to nest it under user like the answer suggests.
Here is the relevant part of my controller:
# GET /users/1
# GET /users/1.json
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #user }
end
end
# PUT /users/1
# PUT /users/1.json
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { }
else
format.html { render action: "edit" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
I don't understand why this is so difficult. I'm just trying to update a name via JSON ...
For the controller you want to post to:
protect_from_forgery with: :null_session
or like from: How do I bypass protect_from_forgery in Rails 3 for a Facebook canvas app?
skip_before_filter :verify_authenticity_token, :only => [THE ACTION]
EDIT
Your JSON is incorrect... you are posting
{"name"=>"Jeff", "id"=>"1"}
Since your controller does user.update_attribute(params[:user]), your JSON needs to be under a user attribute
{
"id": 1,
"user": {
"name": "Jeff"
}
}
This will create a hash of
{"user"=>{"name"=>"Jeff"}, "id"=>"1"}
Authlogic seems to be ignoring the password parameter when creating a new user. Here's my users_controller class:
class Api::V1::UsersController < ApplicationController
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.json { render :json => #user, :status => :created}
else
format.json { render :json => #user.errors, :status => :unprocessable_entity }
end
end
end
private
def user_params
params.require(:user).permit(:username, :email, :password)
end
end
And my user model:
class User < ActiveRecord::Base
acts_as_authentic do |c|
c.require_password_confirmation = false
end
end
When I send a POST request to /api/v1/users/ with a username, email and password parameter, authlogic says that the password cannot be blank even though it isn't. Here's whats printed out by rails:
Started POST "/api/v1/users/" for 127.0.0.1 at 2013-06-22 00:03:30 -0400
Processing by Api::V1::UsersController#create as */*
Parameters: {"email"=>"someemail#website.com", "password"=>"[FILTERED]", "username"=>"myUser", "user"=>{"username"=>"myUser", "email"=>"someemail#website.com"}}
(0.2ms) BEGIN
User Exists (0.4ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER('someemail#website.com') LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."username") = LOWER('myUser') LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."persistence_token" = '7b72bab3627914d33e83e4efe1c5a9dab190750efb227698c8b5b6be7a7ccf118160d8e12623078543e0f4e5f31eb30828799cb0d97fb2af195daee894c79902' LIMIT 1
(0.2ms) ROLLBACK
Completed 422 Unprocessable Entity in 33ms (Views: 0.2ms | ActiveRecord: 3.2ms)
I'm using the latest authlogic and Ruby 2/Rails 4.
Take a look at an excerpt from Rails log:
{"email"=>"someemail#website.com", "password"=>"[FILTERED]", "username"=>"myUser", "user"=>{"username"=>"myUser", "email"=>"someemail#website.com"}}
It looks like you send slightly wrong parameters. To be recognized by Authlogic, password parameter should go under user key in parameters hash. I.e. that line from Rails log should look like this (pay attention to the end of string):
{"email"=>"someemail#website.com", "password"=>"[FILTERED]", "username"=>"myUser", "user"=>{"username"=>"myUser", "email"=>"someemail#website.com", "password" => "[FILTERED]"}}
To fix it, you can do a hack like this:
private
def user_params
params.require(:user).permit(:username, :email).merge(:password => :password)
end
Alternatively, you can adjust the parameters sent from the client side (for example, using user[password] parameter's name instead of just password when sending HTTP POST request).
try this out:-
acts_as_authentic do |config|
config.check_passwords_against_database = false
config.validate_password_field = false
config.crypted_password_field = false
end