关于前端:前端学-Ruby唐诗API项目

40次阅读

共计 10039 个字符,预计需要花费 26 分钟才能阅读完成。

前言

我想了半天,该做什么我的项目,基于笔者的数据库常识羸弱,怕一方面做前端一方面做后端会搞得四不像,又累工夫又长。所以就想以做纯 API 为目标,只做接口会不会更快一些呢

注释

笔者打算做一个全唐诗的 API 我的项目,此我的项目只为学习 ruby on rails web 开发并部署至服务器,会逐渐从零开始到部署上线,部署伎俩会有些原始,不过没事,下个我的项目笔者会降级部署伎俩

先新建一个 API 我的项目

rails new --api --database=postgresql --skip-test tangpoetry

意思是新建一个唐诗的 API 我的项目,数据库为 postgresql,跳过测试

新建后进入我的项目,并更新 gem 下载源

cd tangpoetry
bundle config mirror.https://rubygems.org https://gems.ruby-china.com

从新下载依赖

bundle install --verbose # verbose:打印下载依赖过程

再去 config/database.yml 中批改开发环境时的数据库

development:
  <<: *default
  database: tangpoetry_dev
  username: tangpoetry
  password: 123456
  host: localhost

在此之前,须要在 pgAdmin4(postgresql 图形界面)中创立 database、username、password 等,这里不做赘述

咱们在本地启动服务

rails s

如此,rails 利用就启动了

建设数据库

笔者要做的是全唐诗的 API 接口,要有什么性能先不说,起码不会本人做数据,在网上找了一个 唐诗的数据库,先导入 mysql 中,能看到它有两个表

咱们先依据表中的字段创立俩模型(Model)

rails g model poetry poet_id:integer content:text title:string 
rails g model poet name:string 

PS:模型(Model)须要是复数。id、created_at、updated_at 会本人创立

此时,就有个问题了,这个我的项目的 sql 是以 mysql 为语法而写的,怎么将它转换为 postgresql 呢?

先不要管这个问题,来设计一下要做的 API

  • 随机获取一首诗:/poetry/random
  • 用诗的题目查问:/poetry/title/ 静夜思
  • 列出这个诗人的所有诗:/poetry/author/ 李白
  • 列出这个诗人的这首诗:/poetry/author/ 张若虚 /title/ 春江花月夜
  • 通过创作数量排名:/poet/list/createnum(没做)

确定好要做的 API 后,咱们就去实现,先在命令行中执行以下代码来创立控制器(Controller)

rails g controller poetry random
# rails g controller 名字  动作

会生成这样的文件:

以上命令的意思是说在创立一个名为 poetry 的类,它的办法为 random。rails 会帮你创立好类和办法以及在路由处创立一个 poetry/random 的路由

批改 poetry_controller.rb 中的内容:

class PoetryController < ApplicationController
  def random
    render json: {resource: 'hello, world'}
  end
end

而后拜访 http://127.0.0.1:3000/poetry/random,就能看到 json 格局的返回值了

拜访 url,利用匹配 route,routes 匹配 controller,controller 操作 model,并返回对应的数据给路由

当初咱们要回到最开始的疑难,怎么把全唐诗中的 sql 转化为 postgresql?

笔者通过一些尝试,发现能够转换,数据是有的,数据结构也统一,无非本来是用 mysql 写的,当初将其改成 postgresql。而当初咱们曾经有数据库的两张表了,只有插入数据即可,用 pgAdmin 也好,用其余工具也罢,用 postgresql 语法把数据插入数据库中

在仓库中别离提供 mysql 的数据tang_poetry.sql 和 postgresql 的数据 tangpoetry.sql

INSERT INTO poets (id,name,created_at,updated_at) VALUES (1,'李世民','2014-06-02 11:47:52','2014-06-02 11:47:52'),(...)

INSERT INTO poetries (id,poet_id,content,title, created_at, updated_at) VALUES (2,1,'塞外悲风切,交河冰已结。瀚海百重波,阴山千里雪。迥戍危战火,层峦引高节。悠悠卷旆旌,饮马出长城。寒沙连骑迹,朔吹断边声。胡尘清玉塞,羌笛韵金钲。绝漠干戈戢,车徒振原隰。都尉反龙堆,将军旋马邑。扬麾氛雾静,纪石功名立。荒裔一戎衣,灵台凯歌入。','饮马长城窟行','2014-06-02 11:47:52','2014-06-02 11:47:52'),(...)

如何导入数据:应用 pgAdmin,能够抉择要导入数据的数据库、右键单击该数据库并抉择“Restore…”选项,之后抉择要导入的数据文件并执行导入操作

实现第一个接口

这个时候,数据库中已有全唐诗的数据,咱们当初要做第一个接口,即获取一首随机诗

首先是获取随机数,其次是依据这个 id 找到这一项

...
  def random
    random = Poetry.find(rand(1...43030))
    render json: {resource: random}
  end
...

ruby on rails 就是这么简略,这样就实现了 random 接口

其余 API

除了 random 接口,咱们还有四个接口,做好后再部署唐诗 API 我的项目就实现了,堪称是半小时实现一我的项目

  • 用诗的题目查问:/poetry/title/ 静夜思
  • 列出这个诗人的所有诗:/poetry/author/ 李白
  • 列出这个诗人的这首诗:/poetry/author/ 张若虚 /title/ 春江花月夜
  • 通过创作数量排名:/poet/list/createnum

前三者都是在 poetry 路由下的拜访,咱们先新建 routes

get 'poetry/random'

+get 'poetry/title/:title', to: 'poetry#title'
+get 'poetry/author/:author', to: 'poetry#author'
+get 'poetry/author/:author/title/:title', to: 'poetry#author_title'

意思是拜访 poetry/title/:title 路由,就是去 poetry_controller 中找 title 办法,并且有个 title 变量(其余两者雷同情理)。再去 poetry_controller 文件,新建titleauthorauthor_title 办法

class PoetryController < ApplicationController
  def random
    random = Poetry.find(rand(1...43030))
    render json: {data: random}
  end
  # /poetry/title/ 静夜思
  def title
    result = Poetry.find_by(title: params[:title])
    render json: {data: result}
  end
  # /poetry/author/ 李白
  def author
    author = Poet.find_by(name: params[:author])
    result = Poetry.where({poet_id:  author[:id]})
    render json: {data: result}
  end
  # /poetry/author/ 张若虚 /title/ 春江花月夜
  def author_title
    author = Poet.find_by(name: params[:author])
    result = Poetry.where({poet_id:  author[:id], title: params[:title]})
    render json: {data: result}
  end
end

又搞定了三个接口,你就说快不快

其中,对 ORM 有所不理解,笔者是在 RailsGuides 查问

Poet.find_by(name: params[:title])
# 只能找满足条件的第一条
Poetry.where({poet_id:  @author[:id]})
# where 条件查问
# 找到满足条件的所有项

接着偶们做最初一个接口,即通过创作数量排名:/poet/list/createnum

 rails g controller poet list

或者不必命令行,间接在 routes.rb 上批改,并新建 poet_controller.rb 文件进行更新

——————————————

淦,卡住了

笔者这里搜到一个相干的教程,奈何 sql 根底太差,还是不会弄。这个接口就不做了

部署

本地开发完结了,现部署上线

之前咱们能在 Heroku 疾速部署,但当初它曾经要免费了,所以笔者决定部署到服务器上,思路是:

先应用本地 docker 部署服务,本地跑通后,再上传源码,通过 Dockerfile 构建运行环境,在运行环境中运行源代码

初试 tangpoetry 镜像

咱们先构建 Dockerfile,上面命令很好了解,就不过都解释

FROM ruby:3.1.3
WORKDIR /app
COPY . .
RUN bundle config mirror.https://rubygems.org https://gems.ruby-china.com

ENV RAILS_ENV=production
RUN bundle config set --local without 'development test'
RUN bundle install
ENTRYPOINT bundle exec puma

而后将它打包成镜像

docker build -t tangpoetry .

基于 tangpoetry 镜像,生成容器

docker run -d --name tangpoetry_container -p 3000:3000 tangpoetry
# -d 后盾启动容器
# --name 容器名
# -p 端口映射

咱们拜访(http://localhost:3000)首页,是能看到 Hi 的

为了测试不便,咱们新建一个根路由,返回一个 json:{message: ‘Hi’}

然而如果拜访所写的任意接口,都会拜访不了,起因很简略,因为 production 环境下的数据库未配置,所以咱们须要再建一个容器,并将唐诗数据导入到此容器中,再通过 docker network 连贯两个容器

也就是说咱们的服务由两个容器组成(后续能够的话能够通过 docker-compose 革新)

当初本地环境用的数据库是本地下载了 postgreSQL,所以咱们须要用 docker 启动 postgresSQL 镜像数据库

创立基于 postgres 的容器

docker run -d --name db-for-tangpoetry  -e POSTGRES_USER=tangpoetry -e POSTGRES_PASSWORD=123456  -e POSTGRES_DB=tangpoetry_production -e PGDATA=/var/lib/postgresql/data/pgdata  -v tangpoetry-data:/var/lib/postgresql/data      --network=network1      postgres:14
# -d 后盾运行
# --name 容器名字叫 db-for-tangpoetry
# -e 环境命令
# -v 数据卷
# --network 应用网络

这里的数据卷和网络都要当时建好

docker volume create tangpoetry-data:创立 tangpoetry-data 数据卷

docker volume ls 可查看数据卷列表

docker network create network1 创立 network1 网络

docker network ls 可查看网络列表

进入(postgresSQL)数据库容器

docker exec -it db-for-tangpoetry bash

连贯 tangpoetry_production 数据库

psql -h localhost -p 5432 -U tangpoetry tangpoetry_production

命令 \l 查看数据库中的表

阐明咱们的数据库创立胜利,当初咱们须要导出本地数据库,并导入到 docker 镜像数据库中

先将本地的数据库导出

pg_dump -U tangpoetry -d tangpoetry_dev > tangpoetry.sql
# pg_dump 导出数据
# -U 用户名
# -d 数据库

再导入到db-for-tangpoetry 容器中

docker exec -i db-for-tangpoetry pg_restore -U tangpoetry -d tangpoetry_production < tangpoetry.sql
# pg_restore 导入数据

笔者输出后显示的如下:

从新编译 tangpoetry 镜像

咱们须要将 tangpoetry 的源码在批改下,配置 config/database.yml 中的 production:

production:
  <<: *default
  database: tangpoetry_production
  username: tangpoetry
  password: <%= ENV["DB_PASSWORD"] %>
  host: <%= ENV["DB_HOST"] %>

再从新打包(再此之前先删除原来的容器和镜像)

docker build -t tangpoetry .

基于 tangpoetry 镜像打包 tangpoetry 容器

docker run -d --name tangpoetry_container -p 3000:3000  -e DB_HOST=db-for-tangpoetry -e DB_PASSWORD=123456 --network=network1 tangpoetry

在容器中创立数据库,并 migrate

docker exec -it tangpoetry_container bin/rails db:create db:migrate

这样再拜访 3000 端口时,咱们就能看到数据了

线上部署

以上咱们曾经测试胜利了本地 docker 部署,先将它推到近程 git 仓库,后续咱们会登录服务器,并 git pull 它,而后构建 tangpoetry 镜像,由此创立 tangpoetry_container 容器

这里遇到的坑让我白了四根头发,第三个问题困扰了我两天并白了两根为数不多的头发

问题一:ruby-china 443 证书过期

bundle install 时,gems 源会 443,提醒相似这样:

Retrying download gem from https://gems.ruby-china.com/ due to error (2/4): Gem::RemoteFetcher::FetchError Net::OpenTimeout: Failed to open TCP connection to gems.ruby-china.com:443 (execution expired) (https://gems.ruby-china.com/gems/racc-1.6.2.gem)

换成阿里源、清华源都不行,笔者第一次部署时「应用不换源」来解决,等了良久才下载好,起初看到这篇文章,先将本地的依赖下载好成缓存,再 bundle 时就从本地拿就好

简略来说就两步:

先在我的项目根目录下执行以下命令

bundle cache
bundle lock --add-platform x86_64-linux
bundle package --all-platforms 

(以上皆为局部截图)

再批改 Dockerfile

...
# 增加缓存到 app 中,这里其实是做了 docker 打包的优化,不做过多介绍
ADD vendor/cache /app/vendor/cache
...
# 通过本地下载依赖
RUN bundle install --local
...

当咱们很快打包后 tangpoetry 镜像后,咱们就以它为根据来构建服务,这里咱们复制本地部署时的代码docker run -d --name tangpoetry_container -p 3000:3000 -e DB_HOST=db-for-tangpoetry -e DB_PASSWORD=123456 --network=network1 tangpoetry,先做测试

此时,咱们的服务器上的 postgres 容器还没创立,咱们先把 ruby on rails 服务部署胜利了,再连贯数据库

问题二:secret_key_base 的报错

但拜访服务器 ip+ 端口,发现拜访不了

通过 docker logs tangpoetry_container 查看报错日志

说 production 环境下的 secret_key_base 不存在

淦,又有个知识点

什么是 secret_key_base?为什么须要这个?为什么本地部署时没呈现这个?

Rails 在我的项目初始化的时候就会在根目录config 下生成 master.keycredentials.yml.enc 两个文件,前者能够了解为外围密钥,后者是通过 Rails 自带的加密办法生成的加密后的数据文件

关系为:

master.key + keys => encrypted
encrypted + master.key => keys

keys 是什么,你须要加密的数据,例如 secret_key_base

咱们在临时文件中的写入咱们的 keys,保留敞开后会生成一个新的 master.keycredentials.yml.enc,并且临时文件会主动删除,把.enc 存在 git 中,master.key 排除在 git 外,这样,他人即便拿到源码,拿不到你的 keys(短少 master.key 解不了)

如何读取 keys 呢?

rails c
# 在命令行中输出 rails c 或者 rails console
# 输出代码
Rails.application.credentials.config # 查看所有的 keys
Rails.application.credentials.secret_key_base #查到 secret_key_base 

如何批改 keys 呢?

笔者应用的是 window,应用 window 自带的 PowerShell,它能长期写进参数

$env:EDITOR="code --wait"
rails credentials:edit

此时会生成一个临时文件,咱们将 demo:12345 批改为 demo:123456,保留并删除临时文件,会发现文件 credentials.yml.enc 产生了变动

也就是说 master.key + keys 会生成一个新的credentials.yml.enc

同理,咱们不能在本地和生成应用一套 keys,Rails 反对多环境密钥

$env:EDITOR="code --wait"
rails credentials:edit  --environment production

会失去两个文件:

config/credentials/production.key(被退出 .gitignore)config/credentials/production.yml.enc

咱们只须要把 production.key 写进服务器环境变量中,就能解决问题二的问题了

$env:RAILS_ENV="production"
rails c
Rails.application.credentials.secret_key_base

最佳实际是什么?

  • 先删除 master.keycredentials.yml.enc,通过 rails credentials:edit 生成一个新的 key 和 enc,复制临时文件中的 secret_key_base
  • rails credentials:edit --environment production 生成生产环境的临时文件,粘贴上一步的 secret_key_base
  • 再删除 master.keycredentials.yml.enc,再生成一个新的 key 和 enc

如此,再服务器上将 RAILS_MASTER_KEY 写进环境变量中,

vim ~/.bash_profile

echo DB_HOST=db-for-tangpoetry
echo DB_PASSWORD=123456
echo RAILS_MASTER_KEY=f78c0868148ca3b1aa64ee9e82c66ef4

执行 source ~/.bash_profile 立刻失效

再次启动容器,此时将 DB_HOST 等用变量模式写入

docker run -d --name tangpoetry_container --network network1 -p 3000:3000  -e DB_HOST=$DB_HOST -e DB_PASSWORD=$DB_PASSWORD -e RAILS_MASTER_KEY=$RAILS_MASTER_KEY tangpoetry

问题三:利用容器连贯不上数据库容器

在此之前,咱们曾经能在服务器 ip+ 端口上能拜访到首页,然而此时咱们还没启动数据库,所以还拜访不到数据库

咱们先启动数据库容器

docker run -d --name $DB_HOST --network network1 -p 5432:5432 -e POSTGRES_USER=tangpoetry -e POSTGRES_PASSWORD=$DB_PASSWORD  -e POSTGRES_DB=tangpoetry_production -e PGDATA=/var/lib/postgresql/data/pgdata  -v tangpoetry-data:/var/lib/postgresql/data      postgres:14

并导入数据

docker exec -i db-for-tangpoetry pg_restore -U tangpoetry -d tangpoetry_production < tangpoetry.sql

回到利用容器,进入容器中,初始化数据库

docker exec -it tangpoetry_container bash # 进入唐诗容器
# 进入容器后
rails db:create

发现拜访不了

也就是说容器之间的拜访成了问题

笔者找了很多材料,找了两天还是没有解决问题,也在 ruby china 上发问,终于在在这篇问答中找到了灵感,我降级了 docker 版本,从 19 降级到了 23,就解决了

以上的命令用以下命令就能实现

docker exec tangpoetry_container bash db:create db:migrate

如此,咱们的我的项目就算胜利上线了

如有问题可拜访我的项目地址:https://github.com/johanazhu/tangpoetry

后续

如果,我是说如果,咱们心愿加上随机一页的成果,或者说每天更新一首诗,本地开发,胜利,推到 git 仓库,并在服务器上删除原有镜像,生成新镜像,再依据新镜像打包

要是我的项目迭代频繁,会不会感觉,好麻烦

这篇文章花了笔者很多工夫,好不容易才上线

如当初 2023 年 6 月份,间隔笔者实现初稿曾经 3 个月,笔者也找到了新的收费的部署形式——fly.io

参考资料

  • Active Record 根底
  • 山竹记账全栈版 Vue 3 + Rails 7+TSX
  • 山竹记账免费版
  • How to run ‘rails credentials:edit’ on Windows 10 without installing a Linux Subsystem
  • Why can’t I connect to Postgres in Docker?
  • Postgresql9.1 suddenly could not connect to server: No route to host
  • Capistrano: ArgumentError: Missing secret_key_base for ‘production’ environment, set this string with bin/rails credentials:edit
  • How to get a Docker container’s IP address from the host
  • Rails + PostgreSQL + Docker
  • caching-all-native-gem-platforms

系列文章

  • 前端学 Ruby:前言
  • 前端学 Ruby:装置 Ruby、Rails
  • 前端学 Ruby:相熟 Ruby 语法
  • 前端学 Ruby:相熟 Rails

正文完
 0