Docker
问:如果开发环境与生产环境系统不一致会不会有 bug ?
答:可能会有 bug(如缺少依赖库、API 不兼容等)
解决方式:尽量保证环境一致,推荐使用 Docker
优点
在 Windows 里运行 Linux 容器
在 macOS 里运行 Linux 容器
在 Linux 里运行 Linux 容器
在所有地方用同一套环境!
缺点
你需要了解容器的概念
你需要做端口映射
你需要做数据卷映射
你需要会 Linux 命令
即,有学习门槛
另外,会占用内存和硬盘
使用 Docker + Vscode
- 使用模板代码
下载我准备好的代码[https://github.com/FrankFang/oh-my-env-1],下载后删除 .git 即可使用
使用 VSCode 打开该目录,按下 Ctrl + Shift + P
输入 reopen in container,回车,如果报错,在命令行输入如下命令
docker network create network1
等待片刻,你就进入了我的开发环境
-
测试命令是否可用
新建终端,输入视频中的命令
确保 ruby、bundle、irb、node 和 npm 等命令可用 -
工作空间
oh-my-env 会自动映射为 /workspaces/oh-my-env
该目录里的文件是内外共享的,性能一般
我提前准备好了 ~/repos 目录,该目录是该容器专属的,性能较好
我默认会在 ~/repos 中工作
需要共享时才会用到 /workspaces/oh-my-env/temp 目录 -
持久化
在 Ctrl + Shift + P 弹出框内输入 rebuild container
相当于重装系统
只有 mounts 中的目录和 oh-my-env 目录被保留 -
现有功能
archlinux、zsh、fzf、rvm + ruby、nvm + node、go、
docker in docker、chezmoi、各种国内加速…… -
如何自定义
打开 .devcontainer/Dockerfile
用 RUN 指令来添加你想要添加的依赖
如 RUN yes | pacman -S fish
然后 rebuild container 即可
更多功能请看 FrankFang/oh-my-docker
新建项目
- 创建目录
mkdir ~/repos/hello
code ~/repos/hello - 切换窗口
关闭 oh-my-env 窗口
使用 hello 窗口 - 原则
每次只打开一个目录
不要一次打开很多目录,会卡
2.搭建后端项目
从无到有创建 Rails API
2.1. 初始化目录
rvm use 3 // 使用 ruby
// 使用国内镜像
gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/
bundle config mirror.https://rubygems.org https://gems.ruby-china.com
// 安装 rails
gem install rails -v 7.0.2.3
// 安装 postgresql 驱动
pacman -S postgresql-libs
cd ~/repos
// rails new创建新项目 --api只使用api模式 --database=postgresql 指定使用postgresql数据库;--skip-test 跳过测试;mangosteen-1 目录名
rails new --api --database=postgresql --skip-test mangosteen-1
// 打开项目
code mangosteen-1
// 开启 server
bundle exe rails server
// 需要关闭 server 请按 Ctrl + C
2.2. 启动数据库
上面我们开启了 server 但是打开报错
原因是没有数据库;
在linux 外的命令行运行如下命令
// docker run 启动新的容器
// -d 不要断开
// \ 这一行没有结束
// --name 容器的名字
// -e 环境变量 POSTGRES_USER 用户名
// -v 新增数据卷
// --network 指定网络
// postgres:14 镜像名称 postgres 版本 14
docker run -d \
--name db-for-mangosteen \
-e POSTGRES_USER=mangosteen \
-e POSTGRES_PASSWORD=123456 \
-e POSTGRES_DB=mangosteen_dev \
-e PGDATA=/var/lib/postgresql/data/pgdata \
-v mangosteen-data:/var/lib/postgresql/data \
--network=network1 \
postgres:14
2.3. 连接数据库
修改项目里的 config/database.yml
development:
<<: *default
database: mangosteen_dev
username: mangosteen
password: 123456
host: db-for-mangosteen
再次运行 bundle exe rails server
2.4 设计数据库
两种思路
自上而下:先想大概,再添细节
自下而上:用到什么加什么,会出现打脸的情况
两种思路可以混合,我们先采用自下而上
工具
g 创建 model 模型 user 模型名称 email 和 name 是需要的字段,我们的用户信息暂时只需要 email 和 name
- 1.建模工具
bin/rails g model user email:string name:string
运行完成后
image.png
会创建两个文件
- create_users.rb
class CreateUsers < ActiveRecord::Migration[7.0]
# 对数据库的修改
def change
# 创建一个表,表的名字是 users,do就相当于一个函数,也就是对这个表的字段变更
create_table :users do |t|
# t.string 添加一个string名字是email
t.string :email
t.string :name
#t.timestamps 会生成两个字段 updated_at(更新时间) 和 created_at(创建时间)
t.timestamps
end
end
end
数据库操作工具:ActiveRecord::Migration
- 同步到数据库:bin/rails db:migrate
反悔命令:bin/rails db:rollback step=1
2.5. 创建路由
- routers.rb
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# 当你发送一个 post 请求到 /users 会调用 users的create方法
post '/users', to: 'users#create'
get '/users/:id', to: 'users#show'
end
创建create 和 show 方法
bin/rails g controller users create show
会创建一个 users_controller.rb
class UsersController < ApplicationController
def create
p '你访问了 create'
end
def show
p '你访问了 show'
end
end
使用命令行访问 /users
curl -X POST http://127.0.0.1:3000/users
控制台会打印出 '你访问了 create'
修改上面的代码创建一个数据
class UsersController < ApplicationController
def create
user = User.new name: 'lifa'
// 如果保存成功就渲染 user,否则就渲染user的错误
if user.save
render json: user
else
render json: user.errors
end
end
def show
p '你访问了 show'
end
end
添加必填校验,校验 email 必填
- user.rb
class User < ApplicationRecord
validates :email, presence: true
end
再次运行
curl -X POST http://127.0.0.1:3000/users
// {"email":["can't be blank"]}#
对 show 进行改造
def show
# 得到id 参数
user = User.find_by_id params[:id]
if user
render json: user
else
head 404
end
问题
rebuild 之后,项目就跑不起来了,是为什么?
答:因为依赖没有被持久化
哪些依赖没有持久化?
ruby 的 gems 没有持久化
archlinux 的 postgresql-libs 没有持久化
如何解决?
-
在容器中运行 code /workspaces/oh-my-env-???/.devcontainer/Dockerfile
文末添加RUN yes | pacman -S postgresql-libs
-
在devcontainer.json 里的mount 里添加
"source=gems,target=/usr/local/rvm/gems,type=volume",
- 重新 rebuild
然后运行bundle install
安装成功后再次运行
bundle exe rails server
搭建后端项目
REST
一种网络软件架构风格
不是标准、不是协议、不是接口,只是一种风格
Roy 于 2000 年在自己博士论文中提到此术语
Roy 曾参与撰写 HTTP 规格文档
怎么做?
- 以资源为中心
- 充分利用 HTTP 现有功能,如动词、状态码、头部字段
- GitHub API[https://docs.github.com/en/rest/repos] 就比较符合 REST,值得学习
REST 风格举例1
请求1:创建 item
POST /api/v1/items
Content-Type: application/json
消息体 {"amount":99, "kind": "income"}
响应 {"resource": {...}} 或 {"errors": {...}}
请求2:创建 item
POST /api/v1/items
Content-Type: application/x-www-form-urlencoded
消息体 amount=99&kind=income
REST 风格举例2
请求:更新 item
PATCH /api/v1/items/1
Content-Type: application/json
消息体 {"amount":"11", "kind": "expense"}
反风格:
POST /api/v1/modify_item?id=1
对方观点:全用 POST 多省事儿
我方观点:有 DELETE 不用非要自己想,多费事儿啊
REST 风格举例3
请求:删除 item
DELETE /api/v1/items/1
反风格:
POST /api/v1/remove_item?id=1
反方观点:POST 省事儿
我方观点:自己想路径,费事儿
REST 风格举例4
请求:获取一个或多个 item
GET /api/v1/items/1
GET /api/v1/items?page=1&per_page=10
GET /api/v1/users/2/items
GET /api/v1/items?user_id=2
GET /api/v1/items?tags_id[]=1&tags_id[]=2
GET /api/v1/items?tags_id=1,2
GET /api/v1/items?sort_by[]=id+asc&sort_by[]=name+desc
GET /api/v1/items?keyword=hi
GET /api/v1/items/search/hi
REST 风格总结
- 尽量以资源为中心
url 里的 items 就是资源 - 尽量使用 HTTP 现有功能
其实响应头里也可以包含内容,但目前的例子都没有用到 - 可以适当违反规则
比如 /api/v1/items/search/hi
白话版总结
看见路径就知道请求什么东西
看见动词就知道是什么操作
看见状态码就知道结果是什么
200 - 成功 201 - 创建成功
404 - 未找到 403 - 没有权限 401 - 未登录
422 - 无法处理,参数有问题 402 - 需付费
412 - 不满足前提条件 429 - 请求太频繁
400 - 其他所有错误,详细原因可以放在 body 里
设计 API
发送验证码
资源:validation_codes
动作:create(POST)
状态码:200 | 201 | 422 | 429
- routes.rb
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
namespace :api do
namespace :vi do
# /api/v1
# 如果后缀有s就 resources
resources :validation_codes
end
end
end
运行 bin/rails routers
会生成
GET /api/vi/validation_codes(.:format) api/vi/validation_codes#index
POST /api/vi/validation_codes(.:format) api/vi/validation_codes#create
GET /api/vi/validation_codes/:id(.:format) api/vi/validation_codes#show
PATCH /api/vi/validation_codes/:id(.:format) api/vi/validation_codes#update
PUT /api/vi/validation_codes/:id(.:format) api/vi/validation_codes#update
DELETE /api/vi/validation_codes/:id(.:format) api/vi/validation_codes#destroy
我们只想要 create 方法,所以我们可以使用 only
resources :validation_codes, only: [:create]
登入登出
资源:session(没有s)
动作:create | destroy(DELETE)
状态码:200 | 422
resource :session, only: [:create, :destroy]
当前用户
资源:me
动作:show(GET)
resource :me, only: [:show]
记账数据
资源:items
动作:create | update | show | index | destroy
update 对应 PATCH,表示部分更新
show 对应 GET /items/:id,用来展示一条记账
index 对应 GET /items?since=2022-01-01&before=2023-01-01
destroy 对应 DELETE,表示删除,一般为软删除
resources :items
标签
资源:tags
动作:create | update | show | index | destroy
resources :tags
打标签
资源:taggings(动词的名词形式)
动作:create | index | destroy
创建数据表
// 创建 ValidationCode 数据表 里面有 email字段是字符串类型,kind表示哪一类的验证码(登录/删除),used_at 表示什么时候用验证码
bin/rails g model ValidationCode email:string kind:string used_at:datetime
// 把创建的数据表同步到数据库
bin/rails db:migrate
创建方法
bin/rails g controller validation_codes create
然后修改我们生成的controller 因为我们需要加/api/v1的前缀,所以首先需要把文件移动到controllers/api/v1 下面,然后在我们的类名上添加 Api::V1::
class Api::V1::ValidationCodesController < ApplicationController
def create
end
end
也可以直接使用命令行
bin/rails g controller Api::V1::Validation_codes create
创建 items 支持分页
- 创建 item 数据表
// amount 金额 notes 文章 (text: 长内容,string: 短字符串) tags_id 标签id
bin/rails g model item user_id:integer amount:integer notes:text tags_id:integer happen_at:datetime
- 修改生成的 数据表
class CreateItems < ActiveRecord::Migration[7.0]
def change
create_table :items do |t|
t.bigint :user_id
t.integer :amount
t.text :note
t.bigint :tags_id, array: true
t.datetime :happen_at
t.timestamps
end
end
end
- 创建 controller
bin/rails g controller Api::v1::Items
- 修改 controller
class Api::V1::ItemsController < ApplicationController
def index
Item.page(1)
end
def create
item = Item.new amount: 1
if item.save
render json: {resource: item}
else
render json: {errors: item.errors}
end
end
end
这个时候我们不能通过 .page获取页面,因为没有这个api,我们可以使用kaminari 或 pagy 库
1). 使用 kaminari
在 Gemfile 里添加
gem 'kaminari'
然后运行 bundle 下载后再运行 bin/rails s 重启项目
注意默认每页里有25条,如果想修改每页的条数,需要使用命令
bin/rails g kaminari:config
把对应的 kaminari_config.rb 文件注释解开,修改成自己需要的
Kaminari.configure do |config|
config.default_per_page = 10
再次重新运行bin/rails s
修改代码通过参数获取分页
- items_controller
def index
items = Item.page params[:page]
render json: { resources: items }
end
网友评论