Received JSON in Rails API is not stored - ruby-on-rails

I am working on a web service in Rails that receives data in JSON format from another Rails application. When the web service receives the data, it reads it but does not insert into the database. This is from the log file:
Processing by OrdersController#create as JSON
Parameters: {"name"=>"dst", "address"=>"dst", "email"=>"sdt"}
[1m[36m (0.2ms)[0m [1mbegin transaction[0m
[1m[35mSQL (0.5ms)[0m INSERT INTO "orders" ("created_at", "updated_at")
What should I do to get this data inside the INSERT statement? Do I need a new json.jbuilder file or something else? What am I missing?
Update: Putting the controller#create code here. Didn't paste it initially because it is the default created from the scaffold generator.
# POST /orders
# POST /orders.json
def create
#order = Order.new(order_params)
respond_to do |format|
if #order.save
format.html { redirect_to #order, notice: 'Order was successfully created.' }
format.json { render :show, status: :created, location: #order }
else
format.html { render :new }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end

order_params is presumably defined somewhere in your OrdersController. Normally model parameters in a request are nested under the resource ({order: {"name"=>"dst", "address"=>"dst", "email"=>"sdt"}) and then in order_params would be
def order_params
params.require(:order).permit(:name, :address, :email)
end
But if you can't change the JSON payload, you could just nix the require(:order) part and call params.permit(:name, :address, :email).
You can read the Rails guide on Strong Parameters here: http://edgeguides.rubyonrails.org/action_controller_overview.html#strong-parameters

Related

How to save Rails input parameters to file

I created a small application using scaffold. I'm posting data via the chrome "PostMan" extension.
This is the default create method in my controller:
# POST /settings
# POST /settings.json
def create
#setting = Setting.new(setting_params)
respond_to do |format|
if #setting.save
format.html { redirect_to #setting, notice: 'Setting was successfully created.' }
format.json { render :show, status: :created, location: #setting }
puts Setting.new(setting_params).to_yaml
else
format.html { render :new }
format.json { render json: #setting.errors, status: :unprocessable_entity }
end
end
end
This is the log in Rails' console:
(0.2ms) begin transaction
SQL (1.0ms) INSERT INTO "settings" ("DetectionOnly", "SecRequestBodyAccess", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["DetectionOnly", "f"], ["SecRequestBodyAccess", "f"], ["created_at", "2016-03-23 18:04:42.642492"], ["updated_at", "2016-03-23 18:04:42.642492"]
I'm trying to only log this transaction into a file and save output in YAML format .
I tried adding this right after #setting.save.
puts Setting.new(setting_params).to_yaml
What am I doing wrong here?
If you want to save the yaml to a separate file, you could open the file in append mode and append the yaml as follows in your create action where you have puts...:
...
settings_param_file = ... # File path
if #setting.save
File.open(settings_param_file, 'a') do |file|
file << Setting.new(setting_params).to_yaml
end
end
Also, using puts in your controller action should be considered invalid. Instead you should use Rails.logger or just logger in your controller. To log a debug message use:
logger.debug { Setting.new(setting_params).to_yaml }

rails generate scaffold command on Windows causes ArgumentError

I used "rails generate scaffold project" to create an new web application. I already did this in the past on Linux and Mac OSX running other versions of rails and ruby and all worked fine, but this time I'm working on Windows 7. Here is my environment
C:\Users\user1\Company>ruby -v
ruby 2.0.0p451 (2014-02-24) [x64-mingw32]
C:\Users\user1\Company>rails -v
DL is deprecated, please use Fiddle
Rails 4.1.0
C:\Users\user1\Company>
after I ran the scaffold command, I ran rake db:migrate and I was able to create my first project successfully. Then I can edit the project, but when I click update, I get the following error message
ArgumentError (When assigning attributes, you must pass a hash as an argument.):
Full server log message
Started PATCH "/projects/1" for 127.0.0.1 at 2014-04-29 05:16:33 -0700
Processing by projectsController#update as HTML
Parameters: {"utf8"=>"√", "authenticity_token"=>"gST6BUQNwOZQDYVj60DXLuFANv1JsM02YAIM+xYwt/M=", "commit"=>"Update project", "id"=>"1"}
project Load (0.0ms) SELECT "projects".* FROM "projects" WHERE "projects"."id"= ? LIMIT 1 [["id", 1]]
(1.0ms) begin transaction
(0.0ms) rollback transaction
Completed 500 Internal Server Error in 10ms
ArgumentError (When assigning attributes, you must pass a hash as an argument.):
app/controllers/projects_controller.rb:44:in `block in update'
app/controllers/projects_controller.rb:43:in `update'
Rendered C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/actionpack-4.1.0/lib/action_dispatch/middleware/templates/rescues/_source.erb (2.0ms)
Rendered C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/actionpack-4.1.0/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.0ms)
Rendered C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/actionpack-4.1.0/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (2.0ms)
Rendered C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/actionpack-4.1.0/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (104.0ms)
Here is my "update" method (as was created automatically by the scaffold command)
# PATCH/PUT /projects/1
# PATCH/PUT /projects/1.json
def update
respond_to do |format|
if #project.update(project_params)
format.html { redirect_to #project, notice: 'Update Successful!' }
format.json { render :show, status: :ok, location: #project }
else
format.html { render :edit }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
I tried removing the "PATCH/" keyword, but no luck. I replaced the whole method with the following (this worked for my other application, but not this time on Windows)
# PATCH/PUT /projects/1
# PATCH/PUT /projects/1.json
def update
#project = Project.find(params[:id])
if #project.update_attributes(params[:project])
flash[:notice] = "Update Successful!"
end
respond_with(#project)
end
but this did not make any difference.
I also tried (I found this by browsing SO)
# PUT /projects/1
# PUT /projects/1.json
def update
#project = Project.find(params[:id])
respond_to do |format|
if #project.update_attributes(params[:project])
format.html { redirect_to #project, notice: 'Update Successful!' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
but no success either
Here are the other working methods (all as automatically generated by "rails generate scaffold" command, and they all work fine)
# GET /projects
# GET /projects.json
def index
#projects = project.all
end
# GET /projects/1
# GET /projects/1.json
def show
end
# GET /projects/new
def new
#project = project.new
end
# GET /projects/1/edit
def edit
end
# POST /projects
# POST /projects.json
def create
#project = project.new(project_params)
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Creation Successful!' }
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
Why is "rails generate scaffold" command not working on Windows and working fine on Linux and Mac OSX?
Update 1
Here are the other methods that were automatically created by "rails generate scaffold" command
class ProjectsController < ApplicationController
before_action :set_project, only: [:show, :edit, :update, :destroy]
<other methods listed above : index, show, new, edit, create, update, and destroy>
private
# Use callbacks to share common setup or constraints between actions.
def set_project
#project = Project.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def project_params
params[:project]
end
end
Working Code after making the changes suggested by Kirti Thorat
This is what worked for me
# PATCH/PUT /projects/1
# PATCH/PUT /projects/1.json
def update
#project = Project.find(params[:id])
respond_to do |format|
if #project.update(project_params)
format.html { redirect_to #project, notice: 'Project was successfully updated.' }
format.json { render :show, status: :ok, location: #project }
else
format.html { render :edit }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_project
#project = Project.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def project_params
params.require(:project).permit(:UnitMgtAddress)
end
You just created a scaffold without any other fields. So your projects table simply has 3 default fields id, created_at and updated_at. Because of which in your update action when you do:
if #project.update(project_params)
Or
if #project.update_attributes(params[:project])
you get error as update and update_attributes require a Hash as an argument and params[:project] is nil. Look at your params hash from server log
{"utf8"=>"√", "authenticity_token"=>"gST6BUQNwOZQDYVj60DXLuFANv1JsM02YAIM+xYwt/M=", "commit"=>"Update project", "id"=>"1"}
It doesn't have a key project.
Possible Solutions
Without adding new fields
If you are not planning to add any new fields in your projects table then there is no point in having an update action as what field would you update on?
With adding new fields
You can add new fields to your projects table like name, duration, etc as per your requirement (By creating a migration to add new fields).
After this you would just need to update the project_params method as below:
def project_params
params.require(:project).permit(:name, :duration)
end

Writing to Rails with a JSON API

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"}

Completed 406 Not Acceptable in 773ms

My client app sends JSON encoded POST to rails server but the server shows 406 error and doesn't respond to json.
UsersController create
# POST /users
# POST /users.json
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render json: #user, status: :created, location: #user }
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
Rails console:
Started POST "/users.json" for 127.0.0.1 at 2013-05-21 16:38:47 +0100
Processing by Devise::RegistrationsController#create as JSON
Parameters: {"user"=>{"name"=>"", "available"=>"true", "email"=>"", "sex"=>"male", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}
WARNING: Can't verify CSRF token authenticity
(0.2ms) begin transaction
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = '' LIMIT 1
(0.2ms) rollback transaction
Completed 406 Not Acceptable in 773ms (ActiveRecord: 6.1ms)
Can anyone help me?
Going to try to answer this a while later in case anyone sees it. I've run into this problem over and over even when I've only had JSON as the format option at the bottom of my controller (like this in your example):
respond_to do |format|
format.json { render json: #user, status: :created, location: #user }
end
Even when calling it explicitly with $.getJSON on my requests which does call it with JSON data types.
The trick I found was calling it with .json on my page name.
$.getJSON('controllername-your-calling.json?', function(json){
//do your stuff...
});
Maybe it helps someone.
You'll need to add respond_to :json, only: [:create] at the top of your controller. You may need to add respond_to :html for the actions that take HTML.

Basic .ajax post to a rails 3.2.2 scaffold generating "WARNING: Can't verify CSRF token authenticity"

I am trying to learn how to post some data using $.ajax through jquery to a simple rails scaffold project. There is one standard scaffold created controller => Images
class ImagesController < ApplicationController
# GET /images
# GET /images.json
def index
#images = Image.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #images }
end
end
# GET /images/1
# GET /images/1.json
def show
#image = Image.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #image }
end
end
# GET /images/new
# GET /images/new.json
def new
#image = Image.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #image }
end
end
# GET /images/1/edit
def edit
#image = Image.find(params[:id])
end
# POST /images
# POST /images.json
def create
#image = Image.new(params[:image])
respond_to do |format|
if #image.save
format.html { redirect_to #image, notice: 'Image was successfully created.' }
format.json { render json: #image, status: :created, location: #image }
else
format.html { render action: "new" }
format.json { render json: #image.errors, status: :unprocessable_entity }
end
end
end
# PUT /images/1
# PUT /images/1.json
def update
#image = Image.find(params[:id])
respond_to do |format|
if #image.update_attributes(params[:image])
format.html { redirect_to #image, notice: 'Image was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #image.errors, status: :unprocessable_entity }
end
end
end
# DELETE /images/1
# DELETE /images/1.json
def destroy
#image = Image.find(params[:id])
#image.destroy
respond_to do |format|
format.html { redirect_to images_url }
format.json { head :no_content }
end
end
end
with one route => resources :images. The database schema consists of one field => t.string :name.
My initial test html file is:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"> </script>
</head>
<body>
<script>
$(document).ready(function(){
$.ajax({
type: 'POST', url: "localhost:3000/images",
data: { name: "johngalt" }
});
});
</script>
</body>
The result from webrick is:
Started POST "/images" for 127.0.0.1 at 2012-04-17 09:50:19 -0500
Processing by ImagesController#create as */*
Parameters: {"name"=>"johngalt"}
WARNING: Can't verify CSRF token authenticity
(0.1ms) begin transaction
SQL (63.5ms) INSERT INTO "images" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", Tue, 17 Apr 2012 14:50:21 UTC +00:00], ["name", nil], ["updated_at", Tue, 17 Apr 2012 14:50:21 UTC +00:00]]
(2.0ms) commit transaction
Redirected to http://localhost:3000/images/7
Completed 302 Found in 81ms (ActiveRecord: 65.6ms)
I'm not sure why name doesn't contain "johngalt". Does this have something to do with the " WARNING: Can't verify CSRF token authenticity"?
Edit
When I use curl:
curl -d "image[name]=johngalt" localhost:3000/images.json
The record is created and the name field contains "johngalt". In essence, I'm trying to figure out the .ajax equivalent of doing what I was able to do in curl?
The CSRF token is automatically added in your post forms when you use the Rails form_for helper to create a form, and is meant to protect users against cross-site request forgery attacks. So, if you are trying to post in a javascript file you won't have access to the token.
You can disable CSRF token authentication for specific actions if you so wish, as long as you understand the consequences.
There are a few ways you can do that, listed here: Turn off CSRF token in rails 3
Edit Looking at your CURL example, it looks like you are ajax posting the wrong data. You are ommiting the 'image' param namespace. Try:
<script>
$(document).ready(function(){
$.ajax({
type: 'POST', url: "localhost:3000/images",
data: { image: { name: "johngalt" } }
});
});
</script>

Resources