I've started to create an Api for my rails application. I am currently creating the Sessions Controller for Log in.
But for some reason I am getting this error
Started DELETE "/api/v1/sessions/?auth_token=6157d3673725013ebddbb5e26e8cd64756949110"
for 127.0.0.1 at 2014-08-29 18:54:18 -0700
ActionController::RoutingError (No route matches [DELETE] "/api/v1/sessions"):
I am not understanding why this is happening. Sign Out seems to work perfectly on the actual web application.
I know it may need an ID according to the rake routes but I'm not sure how to implement this.
API CONTROLLER
module Api
module V1
class SessionsController < ApplicationController
skip_before_filter :verify_authenticity_token,
:if => Proc.new { |c| c.request.format == 'application/json' }
respond_to :json
def destroy
sign_out
render :status => 200,
:json => { :success => true,
:info => "Logged Out",
:data => {} }
end
end
end
end
CONTROLLER
class SessionsController < ApplicationController
def destroy
sign_out
redirect_to root_path
end
end
SESSION HELPER
def sign_out
current_user = nil
cookies.delete(:remember_token)
end
ROUTES
### API Routes
namespace :api, defaults: {format: 'json'} do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :sessions, only: [:new, :create, :destroy]
end
end
RAKE ROUTES
api_v1_sessions POST /api/v1/sessions(.:format)
api/v1/sessions#create {:format=>"json"}
api_v1_session DELETE /api/v1/sessions/:id(.:format)
api/v1/sessions#destroy {:format=>"json"}
From the documentation
You can use resource instead of the resources routes helper. It's used to create routes for a singular resource that you don't access using IDs.
namespace :api, defaults: {format: 'json'} do
namespace :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resource :session, only: [:new, :create, :destroy]
end
end
which will give you
GET /session/new
POST /session
DELETE /session
Related
I've used this series as a starting point for a Rails backend for a work portfolio website. Adapting it has been mostly straightforward, and it's doing what I want it to. The one big problem is that the 'index' and 'show' (read actions) should be available without authentication, while 'create', 'update', and 'delete' (write actions) should require a valid JWT.
Following the approach used to exclude the signup and login routes from authentication, I've tried
skip_before_action :authorize_request, only: [:index, :show]
in the appropriate controller. This will however crash the application, with
NoMethodError (undefined method `works' for nil:NilClass):
app/controllers/works_controller.rb:10:in `index'
While the problem seems apparent - if skipping the authentication action the class doesn't get instantiated - the fix isn't, to me at least. Could anyone please help?
The code for the project is here.
Application controller
class ApplicationController < ActionController::API
include Response
include ExceptionHandler
# called before every action on controllers
before_action :authorize_request
attr_reader :current_user
private
# Check for valid request token and return user
def authorize_request
#current_user = (AuthorizeApiRequest.new(request.headers).call)[:user]
end
end
'Works' controller
class WorksController < ApplicationController
#skip_before_action :authorize_request, only: [:index, :show]
before_action :set_work, only: [:show, :update, :destroy]
# GET /works
def index
#works = current_user.works
json_response(#works)
end
# POST /works
def create
#work = current_user.works.create!(work_params)
json_response(#work, :created)
end
# GET /works/:id
def show
json_response(#work)
end
# PUT /works/:id
def update
#work.update(work_params)
head :no_content
end
# DELETE /works/:id
def destroy
#work.destroy
head :no_content
end
private
def work_params
# whitelist params
params.permit(:title, :nature, :role, :client, :timeframe, :description, :images, :url, :blog_post)
end
def set_work
#work = Work.find(params[:id])
end
end
'Users' controller
class UsersController < ApplicationController
skip_before_action :authorize_request, only: :create
def create
user = User.create!(user_params)
auth_token = AuthenticateUser.new(user.username, user.password).call
response = { message: Message.account_created, access_token: auth_token }
json_response(response, :created)
end
def show
json_response(username: current_user.username)
end
private
def user_params
params.permit(
:username,
:password,
:password_confirmation
)
end
end
'Authentication' controller
class AuthenticationController < ApplicationController
skip_before_action :authorize_request, only: :authenticate
# return auth token once user is authenticated
def authenticate
auth_token =
AuthenticateUser.new(auth_params[:username], auth_params[:password]).call
json_response(access_token: auth_token)
end
private
def auth_params
params.permit(:username, :password)
end
end
'AuthenticateUser' helper
class AuthenticateUser
def initialize(username, password)
#username = username
#password = password
end
# Service entry point
def call
JsonWebToken.encode(user_id: user.id) if user
end
private
attr_reader :username, :password
# verify user credentials
def user
user = User.find_by(username: username)
return user if user && user.authenticate(password)
# raise Authentication error if credentials are invalid
raise(ExceptionHandler::AuthenticationError, Message.invalid_credentials)
end
end
'AuthorizeApiRequest' helper
class AuthorizeApiRequest
def initialize(headers = {})
#headers = headers
end
# Service entry point - return valid user object
def call
{
user: user
}
end
private
attr_reader :headers
def user
# check if user is in the database
# memoize user object
#user ||= User.find(decoded_auth_token[:user_id]) if decoded_auth_token
# handle user not found
rescue ActiveRecord::RecordNotFound => e
# raise custom error
raise(
ExceptionHandler::InvalidToken,
("#{Message.invalid_token} #{e.message}")
)
end
# decode authentication token
def decoded_auth_token
#decoded_auth_token ||= JsonWebToken.decode(http_auth_header)
end
# check for token in `Authorization` header
def http_auth_header
if headers['Authorization'].present?
return headers['Authorization'].split(' ').last
end
raise(ExceptionHandler::MissingToken, Message.missing_token)
end
end
'ExceptionHandler' helper
module ExceptionHandler
extend ActiveSupport::Concern
# Define custom error subclasses - rescue catches `StandardErrors`
class AuthenticationError < StandardError; end
class MissingToken < StandardError; end
class InvalidToken < StandardError; end
included do
# Define custom handlers
rescue_from ActiveRecord::RecordInvalid, with: :four_twenty_two
rescue_from ExceptionHandler::AuthenticationError, with: :unauthorized_request
rescue_from ExceptionHandler::MissingToken, with: :four_twenty_two
rescue_from ExceptionHandler::InvalidToken, with: :four_twenty_two
rescue_from ActiveRecord::RecordNotFound do |e|
json_response({ message: e.message }, :not_found)
end
end
private
# JSON response with message; Status code 422 - unprocessable entity
def four_twenty_two(e)
json_response({ message: e.message }, :unprocessable_entity)
end
# JSON response with message; Status code 401 - Unauthorized
def unauthorized_request(e)
json_response({ message: e.message }, :unauthorized)
end
end
The error message states:
NoMethodError (undefined method `works' for nil:NilClass):
app/controllers/works_controller.rb:10:in `index'
Or to translate that, on line 10 of the works_controller.rb file, we're calling a method called works on nil, which is throwing an error.
Assuming line 10 of the works_controller is
#works = current_user.works
Then the error message is telling us that we're calling works on nil, i.e. we have no current_user.
Either where you assign this code is not working properly, or you're accessing this part of the code without signing in and haven't coded around that. Either way, the current_user variable is returning nil and shouldn't be.
Suppose this my one of admin model
ActiveAdmin.register Theme do
menu :if => proc{ current_admin_user.super_admin?}
after_filter :only => [:create, :update, :destroy] do
expire_action :action => :show
expire_action :action => :index
end
controller do
caches_action :index, :show
end
end
with this code caching is done, but on index page with pagination i am facing issue, that is i am not able to visit another page of Theme.
How to implement something like this
https://github.com/amatsuda/kaminari#creating-friendly-urls-and-caching
this is what fixed the issue:
caches_action :index, :cache_path => Proc.new { |c| c.params }
This will generate new cache for specific paginate param's
Read More
I have the following in my routes:
resource :login, only: [:new, :create, :destroy]
resources :users, only: [:new, :create] do
resources :notes
end
but I would like to have url 'my-domain/login' instead of 'my-domain/login/new' and 'my-domain/register' instead of 'my-domain/users/new'.
How can I do than? I have tried the following but it didn't work:
match 'login' => 'login#new'
match 'register' => 'users#new'
resource :login, only: [:create, :destroy]
resources :users, only: [:create] do
resources :notes
end
This gives me error
undefined local variable or method `logins_path' for #<#<Class:0x007fd99c40c608>:0x007fd99c87bdd8>
while visiting 'my-domain/login'
Try this:
match '/login', to: 'login#new'
match '/register', to: 'users#new'
resource :login, only: [:new, :create, :destroy]
A very good tutorial (dare I say "The Best") for learning Ruby on Rails is railstutorial.org. There is a chapter about making a working login and register page.
UPDATE
If you don't want users to visit /users/new I think your current users resources works:
resources :users, only: [:create] do
resources :note
end
Since it only matches a route for users/create. You can also redirect users to /register if they go to /users/new. The new action in your Users Controller:
def new
redirect_to register_path
end
I'd like to have additional attributes for my User model and don't want to create a separate Profile model.
I'm trying to update custom fields with standart «update» from RESTful set of actions:
class UsersController < ApplicationController
before_filter :authenticate_user!
# ...
def update
#user = User.find(params[:id])
authorize! :update, #user
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
end
And it all goes fine except the fact that the current_user is able to update any user's profile. It seems I can't restrict any User action. I've tried:
can :update, User, :id => user.id
and
cannot :update, User # at all
with no luck. Using Devise 1.5.0 and CanCan 2.0.0.alpha
Here's my ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new(:role => nil) # guest user (not logged in)
can :access, :all
if user.admin?
can :manage, :all
else
can :read, Review
if user.customer?
can :update, User, :id => user.id
can [:create, :update, :destroy], Review, :user_id => user.id
end
end
end
end
Code looks good to me.
What if you try to simplify the second condition first and take out the customer condition? And maybe take out "can :access, :all
Something like:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new(:role => nil) # guest user (not logged in)
if user.admin?
can :access, :all
else
can :read, :all
can :update, :users, :id => user.id
can [:create, :update, :destroy], :reviews, :user_id => user.id
end
end
end
Does your restriction work for Reviews (that user can only edit his own reviews) ?
I have a similar ability file but I always work with a seperate profile model..
I am using Devise 1.4.2, RSpec 2.6.0 and Rails 3.1.0.rc6. My routes.rb looks like this:
scope "(:locale)", :locale => /e(s|n)/ do
resources :demotivideos, :only => [:index, :show]
devise_for :users
namespace "admin" do
resources :demotivideos, :except => [:index, :show]
end
end
I am spec'ing that, when a not logged in user acces new, create or update, he should be redirected to new_user_session_path. For this, I am using the following code
context "when not logged in" do
before(:each) do
sign_out user
end
describe "GET new" do
it "should redirect to new user session" do
get :new
response.should redirect_to(new_user_session_path)
end
end
describe "POST create" do
it "should redirect to new user session" do
post :create, :demotivideo => valid_attributes
response.should redirect_to(new_user_session_path)
end
end
describe "PUT update" do
it "should redirect to new user session" do
put :update, :id => 1, :demotivideo => valid_attributes
response.should redirect_to(new_user_session_path)
end
end
end
All are failing because of the same reason: expected route includes the locale (by default en) but the actual redirect was to the same path without locale. My application controller was modified as told in Rails Guides:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :set_locale
def default_url_options(options={})
logger.debug "default_url_options is passed options: #{options.inspect}\n"
{ :locale => I18n.locale }
end
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
end
What am I doing wrong?
Seems like though Rails Guides uses def default_url_options in Devise you need def self.default_url_options. Don't know the difference, though.