I have a simple Sinatra app with two controllers and api helper
# ApplicationController
class ApplicationController < Sinatra::Base
register Sinatra::ActiveRecordExtension
helpers ApiHelper
configure :production, :development do
enable :logging
end
before do
content_type :json
end
get '/hello' do
{ message: 'Hello!' }.to_json
end
end
# ArticlesController
class ArticlesController < ApplicationController
before do
authenticate!
end
get '/articles' do
articles = Article.all
articles.map { |article| serialize(article) }
end
...
end
# ApiHelper
module ApiHelper
def authenticate!
halt 403, { message: 'Unauthorized!' }.to_json unless authorized?
end
private
def authorized?
request.env['HTTP_AUTHORIZATION'] == 'Bearer 123qweasd'
end
end
When I do
curl -X GET -i -H 'Accept: application/json' http://localhost:4567/hello
to do helth check I get 403 Unauthorized. Why? I don't require authentication in /hello endpoint, only in /articles CRUD endpoints so I don't understand why it authenticates in /hello. According to the docs before block is used to perform some action before other action runs but I don't call authenticate! in before block in ApplicationController. What am I missing?
It turned out that not knowingly I was using ArticlesController as a middleware. My config.ru looked like this.
run ApplicationController
use ArticlesController
which made it so that the authenticate! was called before every request.
I changed my config.ru to this:
map '/' do
run ApplicationController
end
map '/articles' do
run ArticlesController
end
And it works.
I am trying to translate a curl request that works in the terminal for me into ruby code using the http gem.
This is the curl request that gives me back the valid json I want:
curl -X GET --header 'Accept: application/json' --header 'api_key: somekey' --header 'authtoken: sometoken' 'https://cdn.domain.io/v3/content_types/shirts/entries?environment=dev'
With the http gem I try to do this in my ruby script:
HTTP.headers(:Accept => "application/json", :api_key => 'somekey', :authtoken => 'sometoken').get("https://cdn.domain.io/v3/content_types/shirts/entries", :params => { :environment => 'dev'}).body.readpartial
And this gives my back "api_key":["is not valid."]} error from the server
What am I doing wrong? How do I get this to work?
Typhoeus seems to be working out well:
require "typhoeus"
require 'multi_json'
require "awesome_print"
response = Typhoeus::Request.new(
"https://api.domain.io/v3/content_types/shirts/entries?environment=dev",
headers: { api_key: "somekey", access_token: "sometoken",
accept_encoding: "gzip" }
).run
# puts response.body
begin
ap MultiJson.load(response.body, :symbolize_keys => true)
rescue MultiJson::ParseError => exception
p exception.data # => "{invalid json}"
p exception.cause # => JSON::ParserError: 795: unexpected token at '{invalid json}'
end
I am following a tutorial on Udemy and adding an address to my Custom Devise Admin #Show Page.
after completing the nesting and controller modifications I am getting this error in the address controller create redirect.
No route matches {:action=>"show", :admin_id=>"B1CA", :controller=>"admin"} missing required keys: [:id]
My Admin Controller:
class AdminController < ApplicationController
before_action :authenticate_admin!
def show
#admin = Admin.find_by_admin_ident(params[:id])
#addresses = #admin.addresses
end
def index
#admins = Admin.all
end
end
My Address Controller: - Provided full controller incase I have erred somewhere else thats causing this.
class Admin::AddressesController < ApplicationController
before_action :set_address, only: [:show, :edit, :update, :destroy]
# GET /addresses
# GET /addresses.json
def index
#addresses = Address.all
end
# GET /addresses/1
# GET /addresses/1.json
def show
end
# GET /addresses/new
def new
#admin = Admin.find_by_id(params[:admin_ident])
#address = Address.new
end
# GET /addresses/1/edit
def edit
end
# POST /addresses
# POST /addresses.json
def create
#admin = Admin.find_by_id(params[:admin_ident])
#address = Address.new(address_params)
#address.admin = #admin
respond_to do |format|
if #address.save
format.html { redirect_to admin_path, notice: 'Address was successfully created.' }
format.json { render :show, status: :created, location: #admin }
else
format.html { render :new }
format.json { render json: #admin.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /addresses/1
# PATCH/PUT /addresses/1.json
def update
respond_to do |format|
if #address.update(address_params)
format.html { redirect_to #address, notice: 'Address was successfully updated.' }
format.json { render :show, status: :ok, location: #address }
else
format.html { render :edit }
format.json { render json: #address.errors, status: :unprocessable_entity }
end
end
end
# DELETE /addresses/1
# DELETE /addresses/1.json
def destroy
#address.destroy
respond_to do |format|
format.html { redirect_to addresses_url, notice: 'Address was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_address
#address = Address.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def address_params
params.require(:address).permit(:admin_id, :address_ident, :number, :name, :st_type, :unit_apt, :grid, :city, :province, :postal_code)
end
end
I am using custom identifiers as opposed to the standard rails /1 ect.. for id's this is how they are implemented.
Admin: - Exact same implementation in Addresses
class Admin < ActiveRecord::Base
before_create :generate_admin_ident
# Model Relations
has_many :addresses, dependent: :destroy
# Model Validations
validates_uniqueness_of :admin_ident
# Unique Ident Generator
def generate_admin_ident
begin
self.admin_ident = SecureRandom.hex(2).upcase
other_admin = Admin.find_by(admin_ident: self.admin_ident)
end while other_admin
end
# Vanity URL
def to_param
admin_ident
end
end
My form_for: Oddly enough this is not namespaced, but loads the form as follows -
<%= form_for [:admin, #admin, #address] do |f| %>
...
<% end %>
My routes File:
devise_for :admins, controllers: { sessions: 'admins/sessions', registrations: 'admins/registrations', passwords: 'admins/passwords', confirmations: 'admins/confirmations', unlocks: 'admins/unlocks'}
#Nests Addresses to Admin
resources :admins, only: [:index, :show], controller: 'admin' do
resources :addresses, except: [:index], controller: 'admin/addresses'
end
My Controller File Layout
Error Screen Better Errors
Please let me know if you want anymore information, I will do my best to accommodate!
EDIT # 1: - Rails Server Output
Started POST "/admins/B1CA/addresses" for ::1 at 2016-05-26 22:47:52 -0600
Processing by Admin::AddressesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"D2Sq2Pbq2bLxpOxBveSFf/3ivRJcm5jbhw39mnB/C1UnDll8z3fdEJMaf9315fcIaI6o7foTrvWrH7pT2y2BtA==", "address"=>{"admin_id"=>"", "address_ident"=>"", "number"=>"1234", "name"=>"Something", "st_type"=>"St", "unit_apt"=>"101", "grid"=>"SE", "city"=>"Calgary", "province"=>"AB", "postal_code"=>"T2B 5V5"}, "commit"=>"Create Address", "admin_id"=>"B1CA"}
Admin Load (0.3ms) SELECT "admins".* FROM "admins" WHERE "admins"."id" IS NULL LIMIT 1
(0.1ms) BEGIN
Address Exists (0.2ms) SELECT 1 AS one FROM "addresses" WHERE "addresses"."address_ident" = '' LIMIT 1
Address Load (0.1ms) SELECT "addresses".* FROM "addresses" WHERE "addresses"."address_ident" = $1 LIMIT 1 [["address_ident", "9F608A04"]]
SQL (0.1ms) INSERT INTO "addresses" ("address_ident", "number", "name", "st_type", "unit_apt", "grid", "city", "province", "postal_code", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING "id" [["address_ident", "9F608A04"], ["number", "1234"], ["name", "Something"], ["st_type", "St"], ["unit_apt", "101"], ["grid", "SE"], ["city", "Calgary"], ["province", "AB"], ["postal_code", "T2B 5V5"], ["created_at", "2016-05-27 04:47:52.551915"], ["updated_at", "2016-05-27 04:47:52.551915"]]
(0.8ms) COMMIT
Redirected to
Completed 500 Internal Server Error in 7ms (ActiveRecord: 1.7ms)
ActionController::ActionControllerError - Cannot redirect to nil!:
Rake Routes Output for Admin_Addresses:
admin_addresses POST /admins/:admin_id/addresses(.:format) admin/addresses#create
new_admin_address GET /admins/:admin_id/addresses/new(.:format) admin/addresses#new
edit_admin_address GET /admins/:admin_id/addresses/:id/edit(.:format) admin/addresses#edit
admin_address GET /admins/:admin_id/addresses/:id(.:format) admin/addresses#show
PATCH /admins/:admin_id/addresses/:id(.:format) admin/addresses#update
PUT /admins/:admin_id/addresses/:id(.:format) admin/addresses#update
DELETE /admins/:admin_id/addresses/:id(.:format) admin/addresses#destroy
Edit: #2: Error When redirect to path is:** admin_addresses_path(#admin)
ActionController::UrlGenerationError at /admins/B1CA/addresses
No route matches {:action=>"create", :admin_id=>nil, :controller=>"admin/addresses"} missing required keys: [:admin_id]
Edit: 3 - Error when form for is set to below
<%= form_for [#admin, #address] do |f| %>
...
<% end %>
NoMethodError at /admins/B1CA/addresses/new
undefined method `addresses_path' for #<#<Class:0x007fc532933768>:0x007fc534b799a8>
Did you mean? admin_addresses_path
I think you May get wrong params for finding your admin. Change
#admin = Admin.find_by_id(params[:admin_ident])
format.html { redirect_to admin_path, notice: 'Address was successfully created.' }
To:
# admin_id here, not admin_ident
#admin = Admin.find_by_id(params[:admin_id])
format.html { redirect_to #admin, notice: 'Address was successfully created.' }
Or if you want to redirect to admin addresses:
# need to remove " except: [:index]" in your address routes
# routes
resources :addresses, controller: 'admin/addresses'"
# controller
format.html { redirect_to admin_addresses_path(#admin), notice: 'Address was successfully created.' }
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
The route in my system is:
user_show GET /user/show(.:format) user#show.
The controller code is:
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #user}
end
end
_spec.rb
describe 'GET #show' do
it 'should return success' do
get :show, id:#user.id
expect(response).to be_success
end
end
result is:
Failure/Error: expect(response).to be_success
expected success? to return true, got false
In the browser, when I type xxx/user/show, it get error.
ActiveRecord::RecordNotFound in UserController#show
But if I type xxx/user/show?id=31, it shows user with id=31!!
Thanks for #Alex Wayne, I add more information here: I check the routes.rb file:
get "user/show"
get "user/index"
get "user/delete"
get "user/edit"
post "user/update"
resource :users, :path => :user, :as => :user
I personal think my teammate should not write down "get user/show, get user/index...." based on Rails Routing. But I can't change their code. So,
anyone know how to test user/show?id=xxx ? Many thanks~!!!!