Writing to Rails with a JSON API - ruby-on-rails

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

Related

Date failing to update, but returning true for Update (Rails ActiveRecord)

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

Unable to process POST request for create action in controller

I am using paperclip-5.0.0, and Rails 5.0.2.This is my code in controller
class PawsController < ApplicationController
before_action :set_paw, only: [:show, :edit]
def index
#paws = Paw.all
end
def show
end
def new
#paw = Paw.new
end
def edit
end
def create
#paw = Paw.new(paw_params)
respond_to do |format|
if #paw.save
format.html { redirect_to #paw, notice: 'Paw was successfully created.' }
format.json { render :show, status: :created, location: #paw }
else
format.html { render :new }
format.json { render json: #paw.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_paw
#paw = Paw.find(params[:id])
end
def paw_params
params.require(:paw).permit(:avatar,:name) if params[:avatar]
end
end
Avatar here is the column for paw which stores images.In model I have added required validations for paperclip and validation for presence of name.
Problem: In rails console I am successfully able to open the image file from my computer and add it to the paw model i.e. #paw.save returns true in console, but in the browser it redirects me to the new_paw_url with two errors.
Name can't be blank. and
Avatar can't be blank.
There are no exceptions or errors thrown in between the process.I have no idea how should I fix this.
ActiveRecord::SchemaMigration Load (0.2ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by PawsController#create as HTML
Parameters: {"utf8"=>"✓","authenticity_token"=>"zKpb2XFEG4FqX3aVQx9oyyGI9x2NpmF+lmt4eZy/3p3VZr2eWYvHLaEjS5AvisJ6iEbRm61SJkJSuTpMx4c0JA==", "paw"=>{"name"=>"Sheru", "gender"=>"Male", "breed"=>"", "dob"=>"", "avatar"=>#<ActionDispatch::Http::UploadedFile:0x005608576e1f30 #tempfile=#<Tempfile:/tmp/RackMultipart20170323-14343-1o95nqx.jpg>, #original_filename="art.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"paw[avatar]\"; filename=\"art.jpg\"\r\nContent-Type: image/jpeg\r\n">}, "commit"=>"Upload"}
(0.1ms) begin transaction
(0.1ms) rollback transaction
(0.1ms) begin transaction
(0.1ms) rollback transaction
Rendering paws/new.html.erb within layouts/application
Rendered paws/_form.html.erb (15.8ms)
Rendered paws/new.html.erb within layouts/application (17.3ms)
Rendered layouts/_navbar.html.erb (0.6ms)
Completed 200 OK in 326ms (Views: 287.5ms | ActiveRecord: 2.0ms)
Feel free to ask for any other code snippets from me.
Your paw_params method is breaking it. Remove the if statement and it should work. The problem you're having is that the params you're recieving through your controller are nested in the params[paw] hash and not the params hash. Your paw_params if statement should be
params.require(:paw).permit(:avatar,:name) if params[:paw][:avatar]
Look at the console of your params hash and you'll see that Rails will nest all params submitted via a form helper within the class name of the object. In this case it's paw.
"paw"=>{"name"=>"Sheru", "gender"=>"Male", "breed"=>"", "dob"=>"", "avatar"=>#<ActionDispatch::Http::UploadedFile:0x005608576e1f30
More obviously:
params[:name] #nil
params[:paw][:name] #"Sheru"
Also, it's not a good practice to add conditionals for strong_paramaters based on the presence of certain attributes. If someone submits a name without an avatar, the name wont be permitted due to strong_parameters but would give the user poor feedback. Instead you should use validations in your Paw model that requires the presence of both the name and avatar.

Received JSON in Rails API is not stored

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

Once I implement friendly_id, how do I get all actions to play nicely?

I have implemented friendly_id on my PostsController.
This is how my PostsController looks:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
load_and_authorize_resource
def index
#posts = Post.all.order("created_at desc")
end
def show
end
def new
#post = Post.new(parent_id: params[:parent_id])
end
def edit
end
def create
#post = current_user.posts.new(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #post }
else
format.html { render :edit }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_post
#post = Post.friendly.find(params[:id])
# If an old id or a numeric id was used to find the record, then
# the request path will not match the post_path, and we should do
# a 301 redirect that uses the current friendly id.
if request.path != post_path(#post)
return redirect_to #post, :status => :moved_permanently
end
end
def post_params
params.require(:post).permit(:status, :title, :photo, :file, :body, :parent_id)
end
end
There are a few things happening here.
The redirect code that checks for legacy URLs and does a 301 redirect interferes with the other actions of the controller - e.g. edit, as can be seen here:
if request.path != post_path(#post)
return redirect_to #post, :status => :moved_permanently
end
When I try to edit a post, this is what happens:
Started GET "/pnpyo-saddened-at-passing-of-roger-clarke/edit" for 127.0.0.1 at 2014-09-02 04:46:49 -0500
Processing by PostsController#edit as HTML
Parameters: {"id"=>"pnpyo-saddened-at-passing-of-roger-clarke"}
Post Load (1.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."slug" = 'pnpyo-saddened-at-passing-of-roger-clarke' ORDER BY "posts"."id" ASC LIMIT 1
Redirected to http://localhost:3000/pnpyo-saddened-at-passing-of-roger-clarke
Filter chain halted as :set_post rendered or redirected
Completed 301 Moved Permanently in 13ms (ActiveRecord: 1.2ms)
Also, another issue that comes up is that I have the ancestry gem installed. Whenever I try to create a child post, by using a URL similar to this: http://localhost:3000/new?parent_id=pnpyo-saddened-at-passing-of-roger-clarke
This is the error generated:
Started GET "/new?parent_id=pnpyo-saddened-at-passing-of-roger-clarke" for 127.0.0.1 at 2014-09-02 04:47:52 -0500
Processing by PostsController#new as HTML
Parameters: {"parent_id"=>"pnpyo-saddened-at-passing-of-roger-clarke"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
(0.4ms) SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL))) [["user_id", 1]]
Post Load (0.3ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = $1 LIMIT 1 [["id", 0]]
Completed 404 Not Found in 35ms
ActiveRecord::RecordNotFound - Couldn't find Post with 'id'=pnpyo-saddened-at-passing-of-roger-clarke:
() app/controllers/posts_controller.rb:19:in `new'
That line 19 is the new action in the PostsController...i.e.:
#post = Post.new(parent_id: params[:parent_id])
Both issues are connected to the friendly_id implementation.
How do I solve both of these issues once and for all?
Friendly_id
First and foremost, you have to appreciate that friendly_id isn't such a big deal.
It works by changing your .find ActiveRecord method, as well as giving you the ability to use the route helpers with your slugs.
This means any confusion you have shouldn't really be about friendly_id - it should be more to do with your system if anything (if you have it set up correctly, friendly_id shouldn't have to feature in any of your calls etc)
Fix
There are several issues you need to consider:
Redirect
ID
Firstly, don't handle your redirect in your controller.
I believe I answered a question to you previously - you need to handle the redirect in the routes part of your application. Why? That's where the routing occurs.
Trying to handle the redirect in the controller goes against MVC principles (IMO) - you'll be better handling the redirect as follows:
#config/routes.rb
resources :posts, path: ''
get 'posts/:id' => redirect("/%{id}")
#get '/:id', to: 'posts#show' -> NOT NEEDED. Will be covered by the resources directive
#get 'posts/:id', to: 'posts#show' -> NOT NEEDED. Will be covered by redirect
#app/models/post.rb
class Post < ActiveRecord::Base
extend FriendlyId
friendly_id :slug, use: [:slugged, :finders]
end
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def show
#post = Post.find params[:id]
end
end
This will fix all your problems.
If you use the friendly_id finders module, you don't need to distinguish any custom :friendly_id params or anything. It will just ping your database for the id and slug - no big deal.
You're overcomplicating this massively - you just need to redirect any /posts/*** to /:id (which will go to the posts controller anyway). This way, your :id will still be handled in the same way as it would without the friendly_id integration

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.

Resources