I am new to web service and Rails too. I have a doubt in creating API using rails 5. How do I create an API for a rails 5 application? I can find some tutorials for API only applications using rails 5. But I need both API and views in a single rails 5 application. How should I do that?
You can create a new rails project as usual:
$ rails new my_project
$ cd my_project
$ bundle
And then you can use scaffold to generate some code:
$ rails g scaffold Product name:string price:float
And migrate your database:
$ rails db:migrate # => update the database
You can now have a look at app/controllers/products_controller.rb
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
# GET /products
# GET /products.json
def index
#products = Product.all
end
# GET /products/1
# GET /products/1.json
def show
end
# GET /products/new
def new
#product = Product.new
end
# GET /products/1/edit
def edit
end
# POST /products
# POST /products.json
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: #product }
else
format.html { render :edit }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
#product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:name, :price)
end
end
As you can see there's respond_to in the create action. This is how you can respond to different request types.
Read more about it: http://edgeapi.rubyonrails.org/classes/ActionController/MimeResponds.html#method-i-respond_to
I've my own solution to build API very quickly https://github.com/igorkasyanchuk/fake_api at least on the stage of prototyping.
Using this gem I was able to provide a skeleton of the API for Frontend team, later it was changed to regular rails controllers.
While create a RESTful API, According to Rails 5 release notes, generating an API only application will:
Start the application with a limited set of middleware
Make the ApplicationController inherit from ActionController::API instead of ActionController::Base
Skip generation of view files,
I am listing down article/ Blog post's link read those:
Best Pratices for API only rails app
Rails Api only Web app
Rails Token based authentication: API only rails web app
Must read, Api only rails web app
Read this also, rails api only web app
Restful API with rails
Create a REST Api with Rails
Create a REST Api with Rails 5
Read any one of the above blog and just go through others for getting additional details or for the knowledge purpose
Have a Great Coding! 😀👨💻👩💻
Just build you regular rails app and when you need a API behavior on that method just respond with a json like this
def index
#cards = Card.all
render json: { status: 'Success', message: 'Loaded all cards', data: #cards }, status: :ok
end
If you really need the additional behavior from the controller to you can just take a regular controller and change the inheritance to something like this
class CardsController < ActionController::API
#your code
end
For more information on this specifically checkout Rails API Applications
Related
In my Rails project, I ran:
rails generate scaffold car
which successfully created all of the necessary files and directories for the application. This includes the standard application.html.erb layout file, as well as the app/views/cars directory that includes more standard html.erb files (index, show, ETC.)
In addition, my cars route is in place in config/routes.rb.
My next command is rails server, which is also successful.
When I open my browser and go to localhost:3000, the welcome page for ruby on rails pops up as normal. I'm expecting that when I go to localhost:3000/cars, I'll see the html rendered from app/views/cars/index.html.erb. If not, I at least expect that it will default to application.html.erb.
Instead, I am repeatedly receiving the following:
>ActionController::UnknownFormat in CarsController#index
>CarsController#index is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: []
My controller class looks like this:
class CarsController < ApplicationController
before_action :set_car, only: [:show, :edit, :update, :destroy]
# GET /cars
# GET /cars.json
def index
#cars = Car.all
end
# GET /cars/1
# GET /cars/1.json
def show
end
# GET /cars/new
def new
#car = Car.new
end
# GET /cars/1/edit
def edit
end
# POST /cars
# POST /cars.json
def create
#car = Car.new(car_params)
respond_to do |format|
if #car.save
format.html { redirect_to #car, notice: 'Car was successfully created.' }
format.json { render :show, status: :created, location: #car }
else
format.html { render :new }
format.json { render json: #car.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /cars/1
# PATCH/PUT /cars/1.json
def update
respond_to do |format|
if #car.update(car_params)
format.html { redirect_to #car, notice: 'Car was successfully updated.' }
format.json { render :show, status: :ok, location: #car }
else
format.html { render :edit }
format.json { render json: #car.errors, status: :unprocessable_entity }
end
end
end
# DELETE /cars/1
# DELETE /cars/1.json
def destroy
#car.destroy
respond_to do |format|
format.html { redirect_to cars_url, notice: 'Car was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_car
#car = Car.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def car_params
params.fetch(:car, {})
end
end
I'd greatly appreciate any insight into this issue. I've followed every bit of advice I've seen on the internet regarding layouts, controllers, and views, and I'm having no luck.
Thanks in advance!
Thanks to everyone who was trying to answer my question! It was a stumper, but I've discovered the problem:
After uninstalling and reinstalling rails a few times, I created a dummy rails project and generated a new scaffold "car." Upon running rails server and loading up localhost:3000/cars, the default layout was rendered as normal. Thinking that reinstalling rails had solved it, I then linked the dummy project to a remote github repository.
At this point, running rails server and loading localhost:3000/cars showed me the same error message as before. This made me believe that something about the remote repo was messing up my project, so I googled around for this error message and github.
I stumbled upon the following link which ultimately answered my question:
https://github.com/rails/rails/issues/18660
Turns out, one of the directories in my root path was capitalized, and that was throwing rails off right away. As soon as I renamed the capitalized directory to all lowercase, the app ran flawlessly.
I'm frustrated by ruby on rails' extremely opinionated conventions, and especially since this project is almost a week late due to such an esoteric mistake. But I'm over the moon to have it running smoothly now and I hope this may help someone else in the future.
Thanks again all!
I have an issue with rails with naming convention.
I have a database where i can't rename table so names are not in plural with inflector.
Today i wanted create model and controller for the table "wishlist__c" and the issue is here. I tried 3 times first by duplicating product model, controller.... and changing name then creating files myself and i still got the issue and then with rails g scaffold wishlist__c
The first error when i try to go to url:8080/wishlist__c/index :
Routing Error
uninitialized constant WishlistCController
wishlist__c_controller.rb exist. I notice after many test that the double '__' is a problem in rails. I rename it to wishlist_c_controller and the same with the model. the error message change to
--Solution: I forget to rename folder wishlist__c to wishlist_c in views folder
Thanks you all ! --
ActiveRecord::RecordNotFound in WishlistCController#show
Couldn't find WishlistC with 'id'=index
the code display under this is from wishlist_c_controller.rb:
def set_wishlist__c
#wishlist__c = ::WishlistC.find(params[:id])
end
How to solve it. I need to link my app to this table
edit:
Model wishlist_c.rb:
class WishlistC < ApplicationRecord
self.table_name = "wishlist__c"
end
wishlist_c_controller:
class WishlistCController < ApplicationController
before_action :set_wishlist__c, only: [:show, :edit, :update, :destroy]
# GET /wishlist__c
# GET /wishlist__c.json
def index
#wishlist__c = WishlistC.all
end
# GET /wishlist__c/1
# GET /wishlist__c/1.json
def show
end
# GET /wishlist__c/new
def new
#wishlist__c = WishlistC.new
end
# GET /wishlist__c/1/edit
def edit
end
# POST /wishlist__c
# POST /wishlist__c.json
def create
#wishlist__c = WishlistC.new(wishlist__c_params)
respond_to do |format|
if #wishlist__c.save
format.html { redirect_to #wishlist__c, notice: 'Wishlist c was successfully created.' }
format.json { render :show, status: :created, location: #wishlist__c }
else
format.html { render :new }
format.json { render json: #wishlist__c.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /wishlist__c/1
# PATCH/PUT /wishlist__c/1.json
def update
respond_to do |format|
if #wishlist__c.update(wishlist__c_params)
format.html { redirect_to #wishlist__c, notice: 'Wishlist c was successfully updated.' }
format.json { render :show, status: :ok, location: #wishlist__c }
else
format.html { render :edit }
format.json { render json: #wishlist__c.errors, status: :unprocessable_entity }
end
end
end
# DELETE /wishlist__c/1
# DELETE /wishlist__c/1.json
def destroy
#wishlist__c.destroy
respond_to do |format|
format.html { redirect_to wishlist__c_index_url, notice: 'Wishlist c was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_wishlist__c
#wishlist__c = WishlistC.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def wishlist__c_params
params.fetch(:wishlist__c, {})
end
end
Rails create RESTful routes based on controller and model. One of the routes would be get wishlist__c/:id which gets mapped to action show of WishlistCController. so when you hit the URL wishlist__c/index it takes index as the id.
If you want to render index page, create a route get wishlist__c/index and map it to index method of your controller. For the above to work you must hit the URL url:8080/wishlist__c/1 where 1 is your WishList ID. Replace it with values of id column of wishlist__c table.
Looking at your controller, you already have a route get wishlist__c/ mapped to the index method of your controller. So url:8080/wishlist__c/ should render index page for your model.
I am getting the following error on my comments controller in my production environments only (works fine in development). The user flow is as follows: I have jQuery that runs when a specific button is pushed which renders a partial file to add a new comment, a simple form. The respond_to method for the .js request in the controller is for new.js.erb file. This should be relatively easy to do but something is going wrong in the Rails (I am using Rails 4.1.1) code or on my server (Rackspace Cloud Server). The error is below:
ActionController::InvalidCrossOriginRequest in CommentsController#new
Security warning: an embedded tag on another site requested protected JavaScript. If you know what you're doing, go ahead and disable forgery protection on this action to permit cross-origin JavaScript embedding.
I have tried the following code in my comments controller (does not work). It simply renders the .js file in the browser as a text string (javascript does not work).
protect_from_forgery except: :new
skip_before_action :verify_authenticity_token
I have tried removing the protect_from_forgery with: :exception method in the application_controller.rb file but it does not work (just renders the javascript in the browser as a text string).
I have tried replacing "protect_from_forgery with: :exception" with "protect_from_forgery with: :null_session" and this does not work either (gives the same InvalidCrossOriginRequest error above).
I am running out of options to fix this. Again, it's only happening in production. On my local machine (via localhost), everything works fine. The code for my comments controller is below:
class CommentsController < ApplicationController
# before_action :set_comment, only: [:show, :edit, :update, :destroy]
before_action :load_topic
before_action :authenticate_user!
# protect_from_forgery except: :new
# skip_before_action :verify_authenticity_token
# GET /comments
# GET /comments.json
def index
#comments = Comment.all
end
# GET /comments/1
# GET /comments/1.json
def show
end
# GET /comments/new
def new
#comment = Comment.new
end
# GET /comments/1/edit
def edit
end
# POST /comments
# POST /comments.json
def create
#comment = #topic.comments.new(comment_params)
#comment.user_id = current_user.id
respond_to do |format|
if #comment.save
format.html { redirect_to #topic, notice: 'Comment was successfully created.' }
format.json { render :show, status: :created, location: #comment }
format.js
else
format.html { redirect_to #article, alert: 'Unable to add comment' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
format.js { render 'fail_create.js.erb'}
end
end
end
# PATCH/PUT /comments/1
# PATCH/PUT /comments/1.json
def update
respond_to do |format|
if #comment.update(comment_params)
format.html { redirect_to #comment, notice: 'Comment was successfully updated.' }
format.json { render :show, status: :ok, location: #comment }
else
format.html { render :edit }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
# DELETE /comments/1
# DELETE /comments/1.json
def destroy
#comment = #topic.comments.find(params[:id])
#comment.destroy
respond_to do |format|
format.html { redirect_to #topic, notice: 'Comment was successfully deleted.' }
format.json { head :no_content }
format.js
end
end
private
def load_topic
#topic = Topic.find(params[:topic_id])
end
# Use callbacks to share common setup or constraints between actions.
def set_comment
#comment = Comment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comment_params
params.require(:comment).permit(:topic_id, :body, :name)
end
end
Any advice on fixing this issue would be appreciated.
Has this actually happened to real users or are you only seeing this error in your logs/monitoring?
This error tends to happen when crawlers are visiting your site (which obviously doesn't happen in your development environment).
The documentation is suggesting you add these to your controller action:
skip_before_action :verify_authenticity_token, if: :json_request?
protected
def json_request?
request.format.json?
end
However, if this is not the case, I think you actually have a CORS problem. 2 possible reasons:
Is your site available via HTTP and HTTPS? These are different origins!
Do you have multiple domains running this site? Try to inspect/log the request headers and see if there is any difference in the Origins.
You can try reproducing this in development too, if you edit your hosts file and point a domain to your local server.
I have an ApplicationController with a given method in the public area
def current_user
session[:user]
end
and another controller
ObjetosController < ApplicationController
which needs to access such method, and I'm getting a horrible error while running my tests (rake test:functionals):
NameError: undefined local variable or method `current_user' for
I'm using Ruby 2.0.0 and Rails 4.0. I've been travelling Google and StackOverflow for hours and hours and haven't found anything about it.
Maybe someone can give me a hint or help me search for an answer please?
If it helps, if I put crap inside ApplicationController, and run the test, it doesn't complain!!! It's like it was loading another version of this class...
Here my ObjetosController code:
class ObjetosController < ApplicationController
before_action :set_objeto, only: [:show, :edit, :update, :destroy]
# GET /objetos
# GET /objetos.json
def index
#objetos = Objeto.all
end
# GET /objetos/1
# GET /objetos/1.json
def show
end
# GET /objetos/new
def new
#objeto = Objeto.new
end
# GET /objetos/1/edit
def edit
end
# POST /objetos
# POST /objetos.json
def create
#objeto = Objeto.new(objeto_params)
respond_to do |format|
if #objeto.save
format.html { redirect_to #objeto, notice: 'Objeto was successfully created.' }
format.json { render action: 'show', status: :created, location: #objeto }
else
format.html { render action: 'new' }
format.json { render json: #objeto.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /objetos/1
# PATCH/PUT /objetos/1.json
def update
current_user
respond_to do |format|
if #objeto.update(objeto_params)
format.html { redirect_to #objeto, notice: 'Objeto was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #objeto.errors, status: :unprocessable_entity }
end
end
end
# DELETE /objetos/1
# DELETE /objetos/1.json
def destroy
#objeto.destroy
respond_to do |format|
format.html { redirect_to objetos_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_objeto
#objeto = Objeto.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def objeto_params
params.require(:objeto).permit(:nombre, :codigo_sgej)
end
end
Solved it.
In my ApplicationController class I was instantiating some classes dynamically defined for each environment. I was also using some constants defined per environment.
As this app hasn't been released to staging or production yet, I had such values defined only in config/environments/development.rb file. I added them to config/environments/test.rb and now it works!
I lost hours with this problem, I'm really dissapointed with rails (or ruby?) for not showing any error at all...
I have a Ruby on Rails application where you can create 'posts'. I started of by using the scaffold generator to give generate the title which is a string and the body which is the content.
Each 'post' has a url of the id, for example /1, /2, /3, etc.
Is there a way to change that to a string of random characters, for example /49sl, /l9sl, etc?
Update
Here is what I have for the posts_controller.rb
class PostsController < ApplicationController
# GET /posts
# GET /posts.json
def index
#posts = Post.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #posts }
end
end
# GET /posts/1
# GET /posts/1.json
def show
#post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #post }
end
end
# GET /posts/new
# GET /posts/new.json
def new
#post = Post.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #post }
end
end
# GET /posts/1/edit
def edit
#post = Post.find(params[:id])
end
# POST /posts
# POST /posts.json
def create
#post = Post.new(params[:post])
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render json: #post, status: :created, location: #post }
else
format.html { render action: "new" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PUT /posts/1
# PUT /posts/1.json
def update
#post = Post.find(params[:id])
respond_to do |format|
if #post.update_attributes(params[:post])
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post = Post.find(params[:id])
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url }
format.json { head :no_content }
end
end
end
Rails uses the to_param method of an ActiveRecord object in order to resolve it into a URL.
Assuming you have a way to generate these unique ids (referring to it as IdGenerator) you can do the following:
1- Generate this id whenever you persist a Post object and save it to the database, let's say under the column url_id
class Post < ActiveRecord::Base
before_create :generate_url_id
def generate_url_id
self.url_id = IdGenerator.generate_id
end
end
2- Inside your Post model override the to_param method:
class Post < ActiveRecord::Base
def to_param
return url_id
end
end
Now post_path(#post) will resolve to /posts/url_id
By the way, you can use SecureRandom.urlsafe_base64 or look here if you don't have an ID generator yet.
Read more on the documentation for to_param.
I hope these two resources are going to help you :
The gem , named obfuscate_id . It represents the ID in a format like :
http://tennisfans.eu/products/4656446465
Another gem - masked_id . It provides a similar functionality . You are in control with a format of the url creation , defining it in a class . Looking at the source it appears , that this gem uses a strategy of obfuscate_id gem .
You can give your posts random URLs by following these 3 steps:
1- In your model (Post.rb), generate a random string for each post before it is saved. For example,
class Post < ActiveRecord::Base
before_create :generate_url_id
def generate_url_id
self.url_id = SecureRandom.urlsafe_base64
end
end
2- In your model (Post.rb), supply a to_param method to override Rails default URL generation. For example:
class Post < ActiveRecord::Base
def to_param
self.url_id
end
end
3- In your controller (PostsController.rb), use a dynamic finder to find your post by its random string. For instance,
class PostsController < ApplicationController
def show
#post = Post.find_by_url_id(params[:id])
...
end
end
I went ahead and put together a complete example and posted it to Github.
Next to Erez manual way you can use the friendly_id gem, with a unique id as your slug.
class Post < ActiveRecord::Base
# FriendlyId
friendly_id :uid
# Set a unique user id on create
before_save :set_uid, on: :create
def set_uid
self[uid] = rand(36**8).to_s(36)
end
end
Please note that the setting of the uid here does not ensure uniqueness. You certainly need to add some kind of validation, but that whole topic is a different one to google.
Friendly_id is a good solution, if you want to use a gem for it.
Follow this screencast:
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid
(either video or asciicast, as you prefer)
Screencasts by Ryan Bates are really well done.
If you still want another option for id generation, you can try using UUIDs:
https://en.wikipedia.org/wiki/Universally_unique_identifier
And a ruby gem to generate them easily:
https://github.com/assaf/uuid
However, I would ask: Why do you want to make them random anyway? If what you are trying to do
is to avoid one of your users from typing another id in the url and accessing data that is not theirs, then probably you would want to limit access in the controller by scoping the finder, with something like this:
def show
#post = current_user.posts.where(:id => params[:id]).first
respond_to do |format|
format.html # show.html.erb
format.json { render json: #post }
end
end
In this case, current_user is a function that returns the current authenticated user (from session, or whatever you application logic dictates).