今日会以昨日同份专案继续。
再次提醒,API Only,没有view。
总不可能让所有人都可以随意更改别人内容,或随意使用网站功能,所以如一般网页一样,还是要建立一个完整的会员机制。虽然我们可以设定所有资料只有read-only
,但在开发途中也可能其他夥伴需要更改内容,或者未来让只有部分
串接者能使用,验证的功能是一定需要的。
透过API与服务器沟通,是无法以session cookie
来记忆用户登出登入,通常会在请求的Header
或Body
内夹带access token
来处理。最简单的想,就像我们串接API
一样,需要有API KEY
。
我们用最简单的作法,让用户在注册後就可以得到一组token
也就是API KEY
,另外多加一个小功能,让用户登出後,会发一组新的KEY
,提高KEY
的安全性。
API KEY
理所当然需要具有绝对独特性,因为要建立会员系统所以顺便使用Devise
,而如果是要生成更好的API KEY
,可以再找专门的gem
处理。
这边便只简单说明流程,神器等级的gem
,请多阅读其首页,解锁更多使用姿势。
Devise安装
#Gemfile
gem 'devise', '~> 4.8'
$ rails g devise:install
$ rails g devise user
$ rails db:migrate
API token
。我们用常见的方式取名吧。
$ rails g migration add_column_token_to_user
#migrate
class AddColumnTokenToUser < ActiveRecord::Migration[6.1]
def change
add_column :users, :authentication_token, :string
# 加索引与要求unique
add_index :users, :authentication_token, :unique => true
#如果DB已经有其他user在,请多做这一步。
# User.find_each do |user|
# user.generate_authentication_token
# user.save!
# end
end
end
$ rails db:migrate
#User Model
class User < ApplicationRecord
#略...
before_create :generate_authentication_token
def generate_authentication_token
self.authentication_token = Devise.friendly_token
end
end
authentication_token
其实命名好记就好。api_token
、auth_token
都可以。
比命名更重要的是User Model
里的设定那个方法,这样才可以确保用户取得API KEY
,Devise.friendly_token
很明显是Devise
给的方法,也可客制化长度.friendly_token(length = 20) ⇒ Object
。
注册
,登入
,登出
。app/controllers/application_controller.rb
。
class ApplicationController < ActionController::API
before_action :authenticate_user_from_token!
def authenticate_user_from_token!
if params[:auth_token].present?
user = User.find_by_authentication_token( params[:auth_token] )
sign_in(user, store: false) if user
end
end
end
由於一开始用API-Only
建立专案,所以原生application_controller
内是ActionController::API
,非API-Only
需手动建立。
与一般专案设定Devise
很像,有设定这个才有current_user
方法。
store: false
是因为不是一般网页式登入,不需要去记忆於session
。
设定路径routes.rb
。
Rails.application.routes.draw do
devise_for :users
namespace :api do
namespace :v1 do
resources :articles
post "/signup", to: "auth#signup"
post "/login", to: "auth#login"
post "/logout", to: "auth#logout"
end
end
end
建立auth_controller
。
$ rails g controller api/v1/auth
class Api::V1::AuthController < ApplicationController
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, email: user.email}, status: 200
else
render json: { message: "Signup 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: "Login!",
auth_token: user.authentication_token,
user_id: user.id }, status: 200
else
render json: { message: "Email or Password wrong" }, status: 401
end
end
def logout
# 设计使用户重新登入时,authentication_token会换。
current_user.generate_authentication_token
current_user.save!
render json: { message: "See you!"}
#加一点回传值,让用户知道他确实登出了。
end
end
这边可以看到设计上,登入时才秀出KEY
。
可以之後加工,会员还需要某些认证才可以正确登入,才可以看到KEY
。
postman
上测试吧。输入Hendler
与Body
方式很多,可以再查询自己觉得便利的方式,这边一样采用Head
告知给什麽资料,Body
给资料。
Singup
。
Login
。
"auth_token": "BMb-p3usAg3ic6_vRp-V"
如果你跟我的一样,请记得通知我买乐透,这边纪录一下待会会用到。
logout
。要输入的有点不同,就是刚刚纪录的"auth_token": "BMb-p3usAg3ic6_vRp-V"
。
可以再login
一次看看,确定是否token
有换。
来实作谁建立的,谁才能修改吧。
Article
。
$ rails g migration add_column_articles
#migrate
class AddColumnArticles < ActiveRecord::Migration[6.1]
def change
add_reference :articles, :user, foreign_key: true
end
end
$ rails db:migrate
#Article Model
class Article < ApplicationRecord
belongs_to :user
end
#User Model
class User < ApplicationRecord
#略...
has_many :articles
end
记得可以rails c --sandbox
测试一下关联性。
articles_controller.rb
。改写之後,postman
上任何动作都开始要加"auth_token": "user_token"
。
class Api::V1::ArticlesController < ApplicationController
before_action :find_article, only: [:show, :update, :destroy]
before_action :authenticate_user!
#GET
def index
#故意测试只看得到自己建立的。
@articles = current_user.articles
render json:@articles, status: 200
end
#GET
def show
begin @article
render json: @article, status: 200
rescue
render json: {error: "article not found!"}
end
end
#PUT/POST
def create
#用关联性建立。
@article = current_user.articles.new(article_params)
if @article.save
render json: @article, status: 200
else
render json: {erroe: "create failed"}
end
end
#PUT/POST/PATCH
def update
if @article.update(article_params)
render json: @article, status: 200
else
render json: {erroe: "update failed"}
end
end
#DELETE
def destroy
@article.destroy
render json: {message: "DELETE Done!"}
end
private
#规定他人不能操作。
def find_article
@article = current_user.articles.find(params[:id])
end
def article_params
params.require(:article).permit(
:title,
:author,
:description
)
end
end
这边我是用两个帐号互相测试,确认过没问题。
请求的json
如下。
GET
、articles``articles/id
。
{"auth_token": "your token"}
POST
、articles
。
PATCH
、articles/id
。
{"auth_token": "your token",
"article" : {
"title" : "兴奋到模糊",
"author" : "剩最後一个礼拜",
"description" : "加油!加油!加油!加油!"
}
}
DELETE
、articles/id
。
{"auth_token": "your token"}
OK!需有认证才能使用的API完成啦。
虽然这个API
非常简单,可能只适合避免前後端吵架时用,但是至少也是了解大概怎麽运作了。
文件於此:https://github.com/nauosika/API-TEST
今天的leetcode.448 Find All Numbers Disappeared in an Array
老牌考古题?
题目连结:https://leetcode.com/problemsfind-all-numbers-disappeared-in-an-array/
题目重点:Ruby宝宝应该都是一行解决。
# @param {Integer[]} nums
# @return {Integer[]}
def find_disappeared_numbers(nums)
end
p find_disappeared_numbers([4,3,2,7,8,2,3,1]) #=> [5,6]
p find_disappeared_numbers([1,1]) #=> [2]
一般就。
(1..nums.size).to_a - nums
好看一点就
[*1..nums.size] - nums
但就想要手刻一个。
这题其实Constraints
里有说到,元素里没有0,1 <= nums[i] <= n
,那这样阵列会发生一个状况。
a = [2, 3, 1, 4]
2.7.3 :026 > (1..a.size).each do |num|
2.7.3 :027 > puts b.include?(num)
2.7.3 :028 > end
true
true
true
true
b = [2, 2, 1, 4]
2.7.3 :030 > (1..b.size).each do |num|
2.7.3 :031 > puts b.include?(num)
2.7.3 :032 > end
true
true
false
true
=> 1..4
所以我们只需要把发生false
时的num
丢进一个新阵列就好。
def find_disappeared_numbers(nums)
ans = []
(1..nums.size).each do |num|
ans << num unless nums.count(num)
end
ans
end
可是这个答案错误,会耗时太久,因为要一个一个判断。
那我们先建立一个表单,是纪录哪些数字有出现,就是true,没出现就是false,比照原本nums
时,false的才丢进去,减少一个一个判断的时间。
def find_disappeared_numbers(nums)
nums_present = []
ans = []
nums.each do |num|
nums_present[num] = true
end
(1..nums.size).each do |num|
ans << num unless nums_present[num]
end
ans
end
完成!
但我还是当Ruby宝宝就好
>>: DAY08 - 自制MOCK API,让你开发更方便
零基础实战javascript(1)-我直接在B站加上按钮 效果: 在笔记本上加上一个按钮(图中橘色...
前言 这一场 LOL 的掉排位来自於之前自己已经雷了千万场 ---阿峻20190928 在资料的世界...
我很早就开始接触组合语言,但没有学太久,就没有再碰了,当初学组合语言的原因,是觉得组合语言是人与机器...
前言 这篇文章会进行到更多的资料操作 将会处理 Indexing Values 在标签值的处理很重...
使用双色调并不是十麽新鲜的事情,最早可以追溯到 60 年代,传统的单色印刷或之後随着数码技术日渐流...