Andi R.
Andi R.

Reputation: 647

rails: create new object with json and user authentication

I want to create an 'image' object on the rails server by JSON POST. This worked perfectly, but now I added user authentication and it doesn't work anymore. I use the gem 'devise' for authentication. Register, login and logout work perfectly with json.

user.rb:

class User < ActiveRecord::Base
  has_many :images

  attr_accessible :name, :email, :password, :password_confirmation, :remember_me, :encrypted_password

  has_secure_password

  validates :email, format: /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, uniqueness: true

end

images_controller:

class ImagesController < ApplicationController

before_filter :require_login #checks if user is logged in before user accesses images

  respond_to :json

def index
    @images = current_user.images

    respond_to do |format|
      format.html { }
      format.json { render :json => {:success => true,
                      :info => "images",
                      :data => @images } }
    end

  end

  def edit
    @image = current_user.images.find(params[:id])
  end

  def new
    @image = current_user.images.build
  end


  def create
    @image = current_user.images.build(params[:image])

    respond_to do |format|
      if @image.save
        format.html { redirect_to images_path }
        format.json { render :json => @image }
      else
        format.html { render "new" }
        format.json { render json: @image.errors, status: :unprocessable_entity }
      end
    end #respond_to
  end

private

  def require_login
    unless user_signed_in? #in application_controller
      redirect_to login_path,
      alert: "Login first."
    end
  end

  end

sessions_controller:

class SessionsController < ApplicationController

  respond_to :json

  def new
    #empty because no underlying model
  end


  def create
    #user = User.find_by_email(params[:user][:email]) #when JSON: { "user" : { "email":"[email protected]", "password":"asdf" }}
    user = User.find_by_email(params[:email]) #when JSON: { "email":"[email protected]", "password":"asdf" }

    respond_to do |format|
      if user && user.authenticate(params[:password]) #because user has_secure_password
        session[:user_id] = user.id

        format.html { redirect_to images_path, notice: "Login successful!" }
        format.json { render :json => {:success => true,
                      :info => "Logged in",
                      :data => { } } }
      else
        format.html { flash.now.alert = "Wrong email or password"
                    render "new" }
        format.json { render :json => {:success => false, :info => "Wrong email or password" } }
      end
    end
  end



  def destroy

    session[:user_id] = nil
    respond_to do |format|
      format.html { redirect_to root_path, notice: "Logout successful!" }
      format.json { render :json => {:success => true,
                      :info => "Logged out",
                      :data => { } } }
    end

  end


end

rake routes:

      root        /                          pages#home
 new_image GET    /images/new(.:format)      images#new
    images GET    /images(.:format)          images#index
     image GET    /images/:id(.:format)      images#show
           POST   /images(.:format)          images#create
edit_image GET    /images/:id/edit(.:format) images#edit
           PUT    /images/:id(.:format)      images#update
     image DELETE /images/:id(.:format)      images#destroy
     users POST   /users(.:format)           users#create
  new_user GET    /users/new(.:format)       users#new
     login GET    /login(.:format)           sessions#new
  sessions POST   /sessions(.:format)        sessions#create
    logout DELETE /logout(.:format)          sessions#destroy

application_controller:

class ApplicationController < ActionController::Base
  protect_from_forgery

  def current_user
    if session[:user_id]
      @current_user ||= User.find(session[:user_id])
    end
  end

  def user_signed_in?
    current_user.present?
  end

  helper_method :user_signed_in?

end

After logging in, which works (showing images also works with GET method and url http://localhost:3000/images), I cannot create a new image with JSON. This is my request from client (I use Chrome's Simple REST Client):

url:           http://localhost:3000/images
method:        POST
Content-Type:  application/json
               Accept: application/json
Data:          {"image":{ "title":"someTitle","url": "someUrl" }}

Response:

500 Internal Server Error
<h1>Template is missing</h1>
<p>Missing template sessions/new, application/new with {:locale=&gt;[:en], :formats=&gt;[:json], :handlers=&gt;[:erb, :builder, :coffee]}. Searched in:
  * &quot;c:/Users/XXX/workspaceRubyOnRails/my_server/app/views&quot;
  * &quot;c:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/devise-2.2.4/app/views&quot;
</p>

Upvotes: 2

Views: 748

Answers (2)

htanata
htanata

Reputation: 36944

Seems like your JSON request is not authenticated. Because of that, Devise sends you to the sign in page. But you don't have JSON version of your sign in page and therefore you get the Missing template sessions/new error.

You can send your cookies together with the request or even better, use token authentication from Devise so you can do your request with a token for authentication like this:

curl -v -H "Accept: application/json" -H "Content-type: application/json" http://localhost:3000/images?auth_token=<token>

You can find some code examples here: https://github.com/plataformatec/devise/wiki/How-To:-Simple-Token-Authentication-Example

Upvotes: 1

Emile Victor
Emile Victor

Reputation: 933

I think that you need to authenticate via the Chrome Simple REST client - your rails app appears to be differentiating between Chrome itself and the rest client.

I will expand this answer once you have gotten past that hurdle.

Upvotes: 0

Related Questions