I am pretty new to ruby on rails and completely new to designing and implementing REST apis. I have one running and am trying to make a simple post request to save something in my database via curl.
Here are my routes:
GET /api/v1/employees(.:format) api_employees#index
POST /api/v1/employees(.:format) api_employees#create
GET /api/v1/employees/:id(.:format) api_employees#show
PUT /api/v1/employees/:id(.:format) api_employees#update
GET /api/v1/employees/:id/addresses(.:format) api_addresses#index
POST /api/v1/employees/:id/addresses(.:format) api_addresses#create
GET /api/v1/employees/:id/addresses/:address_id(.:format) api_addresses#show
PUT /api/v1/employees/:id/addresses/:address_id(.:format) api_addresses#update
and here is my api_employees controller. I haven't made the addresses controller yet but I am trying to post an employee.
class ApiEmployeesController < BaseApiController
before_filter :find_employee, only: [:show, :update]
before_filter only: :create do
unless #json.has_key?('employee') && #json['employee']['address']
render nothing: true, status: :bad_request
end
end
before_filter only: :update do
unless #json.has_key?('employee')
render nothing: true, status: :bad_request
end
end
before_filter only: :create do
#employee = Employee.find_by_id(#json['employee']['id'])
end
def index
render json: Employee.all
end
def show
render json: #employee
end
def create
if #employee.present?
render nothing: true, status: :conflict
else
#employee = Employee.new
#employee.assign_attributes(#json['employee'])
if #employee.save
render json: #employee
else
render nothing: true, status: :bad_request
end
end
end
def update
#employee.assign_attributes(#json['employee'])
if #employee.save
render json: #employee
else
render nothing: true, status: :bad_request
end
end
private
def find_employee
#employee = Employee.find_by_id(params[:id])
render nothing: true, status: :not_found unless #employee.present?
end
end
I am trying to post using: curl -H "Content-Type: application/json" -X POST -d '{"employee":{"address":"123.123.123.123"}}' http://myapi.heroku.com/api/v1/employees
and I get the response
<body>
<!-- This file lives in public/422.html -->
<div class="dialog">
<div>
<h1>The change you wanted was rejected.</h1>
<p>Maybe you tried to change something you didn't have access to.</p>
</div>
<p>If you are the application owner check the logs for more information.</p>
</div>
Do I need to change the access somehow? Any help is much appreciated.
Just needed skip_before_action :verify_authenticity_token
Related
I'm building an app where users can accept bookings from clients via their own booking page. These unique urls will all be public facing (no auth) and sent to potential clients by the user (this is how my client requested the functionality). When I enter an existing user's booking URL (e.g. https://localhost:3000/users/1/appointments/new) in the browser, the page works perfectly. When I enter the URL for a user that does not exist (e.g. https://localhost:3000/users/5999/appointments/new) I get the following error:
ActiveRecord::RecordNotFound in BookingsController#booking_page
Couldn't find User with 'id'=100
Instead of this error I would like to redirect to the 404 page instead. This is my controller (redirect_to_not_found is not being used, I was testing this in a before_action):
class BookingsController < ApplicationController
before_action :authenticate_user!, except: :booking_page
before_action :set_user, only: :booking_page
layout 'public', only: :booking_page
def booking_page
respond_to do |format|
if #user
format.html { render :booking_page }
format.json { render json: #user, status: :ok }
else
format.html { render(:file => Rails.root.join('public', '404'), :formats => [:html], :status => 404, :layout => false) }
format.json { render json: 'Not Fount', status: :not_found }
end
end
end
private
def redirect_to_not_found
respond_to do |format|
if #user == nil
format.html { render(:file => Rails.root.join('public', '404'), :formats => [:html], :status => 404, :layout => false) }
format.json { render json: 'Not Fount', status: :not_found }
end
end
end
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def bookings_params
params.require(:user_booking).permit(:client_firstname, :client_surname, :client_email, :client_mobile_namber, :services_required, :notes, :date, :start_time, :end_time, :location, :cost, :payment_completed)
end
end
Is there any way I can assign set the #user variable/object before running the booking_page action method and check if the user exists in the database at the same time?
I tried using the accepted answers from here but I still get the same error.
You can add this to your controller to rescue from this error/exception. You can put it in your ApplicationController for app-wide effect or in specific controllers.
rescue_from ActiveRecord::RecordNotFound do |exception|
logger.error "Not found ..."
redirect_to 404_path # You will have to configure this yourself in routes.rb
# ... OR use your method
redirect_to_not_found
end
User.find raises error when record is not in the DB. You can use nil-flavour of finders, e.g. find_by
#user = User.find_by(id: params[:id])
It will set #user to nil if it's not in the DB
I have this following controller for my application:
class Api::BaseApiController< ApplicationController
before_action :parse_request, :authenticate_member_from_token!
def index
render nothing: true, status: 200
end
protected
def authenticate_member_from_token!
if !request.headers[:escambo_token]
#member = Member.find_by_valid_token(:activate, request.headers['escambo_token'])
if !#member
render nothing: true, status: :unauthorized
end
end
end
Then, I have another controller that inherits from that Controller:
class Api::CategoryController < Api::BaseApiController
before_action :find_category, except: [:index]
def index
#category = Category.all
puts(#category)
render json: #category
end
But the controller is allowing requests without the token.
EDIT 1: for some reason the index action started to working normally. But still not doing the validation for the token.
EDIT 2: fixing method from private to protected
Your code needs to render :unauthorized if the token is missing, OR invalid. In other words, you need the code to be along the lines of:
def authenticate_member_from_token!
unless Member.find_by_valid_token(:activate, request.headers['escambo_token'])
render nothing: true, status: :unauthorized
end
end
However, with this code you may find yourself double-rendering in the controller. A cleaner approach could be to instead raise an exception, then rescue from it and render appropriately - e.g.
EscamboTokenInvalid = Class.new(StandardError)
rescue_from EscamboTokenInvalid, with: :escambo_unauthorized
def authenticate_member_from_token!
unless Member.find_by_valid_token(:activate, request.headers['escambo_token'])
raise EscamboTokenInvalid
end
end
def escambo_unauthorized
render nothing: true, status: :unauthorized
end
I have a Rails controller, and I'm using whitelisted params to create new items:
def course_discussion_comment_params
params.require(:course_discussion_comment).permit!
end
Most of the time this works correctly and the logs show me the params hash as
Parameters: {"body"=>"xxx", "course_id"=>"xxx", "course_discussion_id"=>"xxx", "course_discussion_comment"=>{"body"=>"xxx"}}
However, occasionally the params hash doesn't seem to get parsed correctly by Rails, and the request fails, logging a params hash using application instead of course_discussion_comment, like below:
Parameters: {"body"=>"xxx", "course_id"=>"xxx", "course_discussion_id"=>"xxx", "application"=>{"body"=>"xxx"}}
...
ActionController::ParameterMissing (param is missing or the value is empty: course_discussion_comment):
I can't seem to find anything causing it on my end, the issue is intermittent and occurs on other controllers too, but it is consistent in that it has happened multiple times, and in particular this controller seems to be affected the worst, happening every few days and only getting resolved when the server is restarted (it's on Heroku, so dyno is restarted daily).
Any ideas of what might be going on? Here is the full controller:
class CourseDiscussionCommentsController < ApplicationController
before_action :load_course
before_action :load_course_discussion
before_action :set_course_discussion_comment, only: [:update, :destroy]
before_action {
in_course(params[:course_id])
}
before_action only: [:update, :destroy] {
current_user(#course_discussion_comment[:from][:_id])
}
def create
course_discussion_comment_params[:from] = {
_id: #authenticatedUser[:id],
firstName: #authenticatedUser[:firstName],
lastName: #authenticatedUser[:lastName],
}
course_discussion_comment_params[:deleted] = false
#course_discussion_comment = #course_discussion.course_topic_comments.build(course_discussion_comment_params)
if #course_discussion_comment.save
DiscussionActionMailer.comment_added(request.base_url, #course, #authenticatedUser, #course_discussion, #course_discussion_comment).deliver_now
render json: #course_discussion_comment, status: 200
else
render json: #course_discussion_comment.errors, status: 422
end
end
def update
if #course_discussion_comment.update(course_discussion_comment_params)
render json: #course_discussion_comment, status: 200
else
render json: #course_discussion_comment.errors, status: 422
end
end
def destroy
params = {
:deleted => true
}
if #course_discussion_comment.update(params)
render json: #course_discussion_comment, status: 200
else
render json: #course_discussion_comment.errors, status: 422
end
end
private
def course_discussion_comment_params
params.require(:course_discussion_comment).permit!
end
def load_course
#course = Course.find(params[:course_id])
end
def load_course_discussion
#course_discussion = #course.course_topics.find(params[:course_discussion_id])
end
def set_course_discussion_comment
#course_discussion_comment = #course_discussion.course_topic_comments.find(params[:id])
end
end
I am new to build Rails API from scratch (only API not web app) so I can build iOS app to connect to API. What I have an issue on my Rails API project doesn't allow me to have return JSON results, it always return HTML. Any idea what is wrong? Any suggestion appreciated. Thanks!
What I want to have like this:
{"code":12,"title":"User doesn't exist","status":404}
This is what I got (it returns in red header in HTML page):
ActiveRecord::RecordNotFound in Api::V1::UsersController#show
Couldn't find User with 'id'=5
Take a look at my scripts:
routes.rb (require JSON format):
Rails.application.routes.draw do
namespace :api, defaults: { format: :json } do
namespace :v1 do
resources :entries, except: [:new, :edit]
resources :users, except: [:new, :edit]
end
end
end
../app/controllers/api/v1/users_controller.rb
module Api
module V1
class UsersController < ApplicationController
before_action :set_user, only: [:show, :update, :destroy]
def show
if User.exists?(params[:id])
#user = User.find(params[:id])
render json: #user
else
render json: { code: 12, title: "User doesn't exist", status: 404 }, status: :not_found
end
.
.
.
Note: It is very strange how this show method above acting weird. Part 1 is working well to return JSON results but Part 2 returns HTML instead JSON results.
Part 1: render json: #user
Part 2: render json: { code: 12, title: "User doesn't exist", status: 404 }, status: :not_found
../app/controllers/api/v1/application_controller.rb
class ApplicationController < ActionController::API
rescue_from ActionController::ParameterMissing, with: :render_bad_request
rescue_from ActiveRecord::RecordNotSaved, :with => :access_denied
def render_bad_request(e)
render json: params, status: :bad_request
end
end
Use User.where(id: params[:id]).first instead User.exists?(). Is the same query and it going to be cached by Active Record.
Also, i think that you before_action method is the guilty. Try with this
protected
def set_user
#user ||= User.where(id: params[:id]).first
end
...
And in your controller
def show
if #user.present?
render json: #user
else
render json: { code: 12, title: "User doesn't exist", status: 404 }, status: :not_found
end
I searched about this question, but had no success.
I´m trying to learn ruby on rails, came from php. I generated a webapp with the generation tool, second I generated a News controller with scaffold. The devise and pundit are installed too with gems.
The program works perfectly, the problem is related to the News module, I generated it with scaffold.
The routes where created with the command: resources :news
My idea is to create one _form.html.erb and it be called to create a new record or to updated an existing record. Some tutorials teach to create a new.html.erb and an update.html.erb file and duplicate the code, but I know that is possible to have partials as the main form part.
I´m using simple_form_for and the code to do the new is:
# GET /news/new
def new
#news = New.new
authorize New
end
The _form.html.erb
<%= simple_form_for(#news) do |f| %>
<%= f.input :titulo %>
<%= f.input :resumo %>
<%= f.button :submit %>
<% end %>
When I enter to edit, it works, but to add a new it throws.
ActionController::UrlGenerationError at /news/new
No route matches {:action=>"show", :controller=>"news", :locale=>:en} missing required keys: [:id]
Sorry for my bad english, I´m without direction here, is there any way that I can solve it?
Thanks.
====== UPDATED =======
routes.rb
Rails.application.routes.draw do
root to: 'visitors#index'
devise_for :users
resources :users
resources :news
end
New.rb (Model)
class New < ActiveRecord::Base
belongs_to :user
end
application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :set_locale
def default_url_options(options={})
{ locale: I18n.locale }
end
private
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
end
news_controller.rb (Complete)
class NewsController < ApplicationController
before_filter :authenticate_user!
after_action :verify_authorized
#before_action :set_news, only: [:show, :edit, :update, :destroy]
# GET /news
# GET /news.json
def index
#news = New.all
authorize New
end
# GET /news/1
# GET /news/1.json
def show
#news = New.find(params[:id])
authorize New
end
# GET /news/new
def new
#news = New.new
authorize New
end
# GET /news/1/edit
def edit
#news = New.find(params[:id])
authorize New
end
# POST /news
# POST /news.json
def create
#news = New.new(news_params)
respond_to do |format|
if #news.save
format.html { redirect_to #news, notice: 'New was successfully created.' }
format.json { render :show, status: :created, location: #news }
else
format.html { render :new }
format.json { render json: #news.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /news/1
# PATCH/PUT /news/1.json
def update
respond_to do |format|
if #news.update(news_params)
format.html { redirect_to #news, notice: 'New was successfully updated.' }
format.json { render :show, status: :ok, location: #news }
else
format.html { render :edit }
format.json { render json: #news.errors, status: :unprocessable_entity }
end
end
end
# DELETE /news/1
# DELETE /news/1.json
def destroy
#news.destroy
respond_to do |format|
format.html { redirect_to news_url, notice: 'New was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_news
#news = New.find(params[:id])
end
private
def news_params
params.require(:news).permit(:titulo, :resumo, :texto, :published_at, :user_id)
end
end
Command rake routes
news_index GET /news(.:format) news#index
POST /news(.:format) news#create
new_news GET /news/new(.:format) news#new
edit_news GET /news/:id/edit(.:format) news#edit
news GET /news/:id(.:format) news#show
PATCH /news/:id(.:format) news#update
PUT /news/:id(.:format) news#update
DELETE /news/:id(.:format) news#destroy
Thanks in advance.
======= UPDATE 2 ===========
Changing my New action to this:
def new
#news = New.create(params[:id])
end
It solved, but everytime I enter, it creates an empty record...
Use news_index_path for GET /news and POST /news. Rails doesn't figure out the pluralization correctly for the "news" term.
Check the output of rake routes, it will be obvious.