话说,好记性不如烂笔头「当然,主要是自己笨」,趁着假期,实作了一个简单的API接口,主要是CRUD + auth 认证部分,以加强对API接口部分的理解。
正文
以用户对一个event活动进行CRUD操作的功能实现为例:
主要按如下步骤走:
建model event,含字段title, description
设置路由,实作event的get, post, patch ,delete接口
添加认证API
最后的效果是:
用户可以通过API接口来新建,修改,查看,删除event
用户在发出新建,修改,删除请求时,需要认证
P.S:其实可以加个 rspec测试,不过这里我用postman直接测了,省去rspec部分,以免篇幅过长「主要还是因为,呃,你懂的,懒……」
好,开搞吧。
一、新建一个project event_api
rails new event_api
cd event_api
git init
生成如下文件:
二、建event model,实作CRUD接口
建event model, 终端输入:
git checkout -b event # 切换分支,做烂了可以再开个 rails g model event title:string description:text uuid:string rake db:migrate
「为了安全性,给event加上一个uuid的字段」
在
app/models/event.rb
中,给title加上有效性验证,并在创建时自动生成uuid:class Event < ApplicationRecord validates_presence_of :title before_validation :generate_uuid, :only => :create def to_param self.uuid end protected def generate_uuid self.uuid = SecureRandom.uuid end end
设置路由,在
config/routes.rb
中添加如下路由:namespace :api, :defaults => {:format => :json } do namespace :v1 do resources :events, :only => [:index, :show, :create, :update, :destroy] end end
产生API controller,终端输入:
rails g controller api --no-assets
修改
app/controllers/api_controller.rb
:- class ApiController < ApplicationController + class ApiController < ActionController::Base end
改成继承自ActionController::Base,因为API 不需要
protect_from_forgery with: :exception
这一行的 CSRF 浏览器安全检查。实作event的get, post, patch, delete
终端执行:
rails g controller api::v1::events --no-assets
在
app/controllers/api/v1/events_controller.rb
中,填入以下内容:class Api::V1::EventsController < ApiController def index @events = Event.all render :json => { :data => @events.map {|event| { :id => event.id, :uuid => event.uuid, :title => event.title, :event_url => api_v1_event_url(event.id) }} } end def create @event = Event.new( :title => params[:title], :description => params[:description]) if @event.save render :json => { :id => @event.id, :uuid => @event.uuid, :title => @event.title, :description => @event.description } else render :json => { :message => "创建event失败", :errors => @event.errors }, :status => 400 end end def show @event = Event.find_by_uuid!(params[:id]) render :json => { :id => @event.id, :uuid => @event.uuid, :title => @event.title, :description => @event.description } end def update @event = Event.find_by_uuid!(params[:id]) @event.update(:title => params[:title], :description => params[:description]) render :json => {:message => "更新event成功"} end def destroy @event = Event.find_by_uuid!(params[:id]) @event.destroy render :json => {:message => "删除event成功"} end end
注意,将EventsController 修改为继承自ApiController
启服务器,
rails s
, 用postman测试看看:create
index
show:
Update:
发出get请求,看到event的详情中已经修改了title:
destroy:
看event的list, 发现event2018_updated已经删除:
OK!!
三、添加认证API
这里做event的API时候,都不需要认证的,让我们加上认证部分。
装上devise
编辑
Gemfile
加上gem "devise"
终端运行:
bundle
, 重启服务器,终端执行:rails g devise:install rails g devise user rake db:migrate
给event和user挂上关系
在
app/models/user.rb
加上:has_many :events
在
app/models/event.rb
加上:belongs_to :user
将user_id添加到event中,终端执行:
rails g migration add_user_id_to_events user_id:integer rake db:migrate
修改
app/controllers/api/v1/events_controller.rb
, 在create部分填上@event.user = current_user...... def create @event = Event.new( :title => params[:title], :description => params[:description]) @event.user = current_user if @event.save render :json => { :id => @event.id, :uuid => @event.uuid, :title => @event.title, :description => @event.description } else render :json => { :message => "活动创建失败", :errors => @event.errors }, :status => 400 end end ......
这里也可以设置一下show,index部分,添加上creator的字段,显示是谁创建这个event的。「此处略去,不是重点」
给user加上token字段
终端运行:
rails g migration add_auth_token_to_users
编辑文件
XXXX_add_auth_token_to_users.rb
文件:class AddAuthTokenToUsers < ActiveRecord::Migration[5.1] def change + add_column :users, :auth_token, :string + add_index :users, :auth_token, :unique => true end end
终端运行:
rake db:migrate
编辑
app/models/user.rb
, 给user加上token:class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable has_many :events before_create :generate_authentication_token def generate_authentication_token self.auth_token = Devise.friendly_token end end
实作注册,登录,登出的API
在
config/routes.rb
加上路由:...... namespace :api, :defaults => {:format => :json } do namespace :v1 do resources :events, :only => [:index, :show, :create, :update, :destroy] + post "/signup" => "auth#signup" + post "/login" => "auth#login" + post "/logout" => "auth#logout" end end ......
生成auth的controller:
rails g controller api::v1::auth --no-assets
编辑它:
class Api::V1::AuthController < ApiController before_action :authenticate_user!, :only => [:logout] def signup user = User.new(:email => params[:email], :password => params[:password]) if user.save render :json => {:user_id => user.id } else render :json => {:message => "Failed", :errors => user.errors}, :status => 400 end end def login if params[:email] && params[:password] user = User.find_by_email(params[:email]) end if user && user.valid_password?(params[:password]) render :json => { :message => "ok", :auth_token => user.auth_token, :user_id => user.id } else render :json => {:message => "Email or Password is wrong"}, :status => 400 end end def logout current_user.generate_authentication_token current_user.save! render :json => {:message => "logout successful"} end end
编辑
app/controllers/api_controller.rb
, 添加如下验证:before_action :authenticate_user_from_token! def authenticate_user_from_token! if params[:auth_token] user = User.find_by_auth_token(params[:auth_token]) sign_in(user, store: false) if user end end
这里可以改成将auth_token放在了header中传递,看这篇如何将auth token放入headers进行传递?
重启服务器,用postman测试看看:
login ,获取auth_token
logout, 传递auth_token
最后,给event添加上认证,修改
app/controllers/api/v1/events_controller.rb
中,添加:...... before_action :authenticate_user!, :only => [:create, :update, :destroy] ....
用postman测试看看,现在新建event需要传递一个auth_token才行:
添加上auth_token, 创建成功:
Patch, delete同理,可以用postman 进行测试看看,这里就不细看了。
The End
我这边列出来的都是非常基础的API接口,其他复杂点的都是可以按照这个方式来做的,比如你可以在这个的基础上,添加comment,让用户通过接口给event做评论,点赞,自定义like method等等。
对于routes的写法,上面我用了rails自身的resource方式:
resources :events, :only => [:index, :show, :create, :update, :destroy]
, 也用了较易于理解的逐条列出路径的方式:post "/signup" => "auth#signup"
, 可根据实际情况自行选择.Happy coding! : )