快速实现后端接口
快速实现读取API
get /api/v1/tags
- 创建model
进入ruby3的nvm use 3
,然后在执行创建model,bin/rails g model tag user:references name:string sign:string deleted_at:datetimee
class CreateTags < ActiveRecord::Migration[7.0]
def change
create_table :tags do |t|
# foreign_key为外键
t.references :user, null: false, foreign_key: false
t.string :name, null: false
t.string :sign, null: false
t.datetime :deleted_at
t.timestamps
end
end
end
修改好表结构后在执行bin/rails db:migrate
,然后可以在schema.rb
文件查看该表结构。
- 创建controller——执行
bin/rails g controller api/v1/tags_controller
- 写测试
require 'rails_helper'
RSpec.describe "Api::V1::Tags", type: :request do
describe "获取标签列表" do
it "未登录获取标签" do
get '/api/v1/tags'
expect(response).to have_http_status(401)
end
it "登录后获取标签" do
user = User.create email: '1@qq.com'
another_user = User.create email: '2@qq.com'
# 创建 11 个 tag
11.times do |i| Tag.create name: "tag#{i}", user_id: user.id, sign: 'x' end
11.times do |i| Tag.create name: "tag#{i}", user_id: another_user.id, sign: 'x' end
get '/api/v1/tags', headers: user.generate_auth_header
expect(response).to have_http_status(200)
json = JSON.parse response.body
expect(json['resources'].size).to eq 10
get '/api/v1/tags', headers: user.generate_auth_header, params: {page: 2}
expect(response).to have_http_status(200)
json = JSON.parse response.body
expect(json['resources'].size).to eq 1
end
end
end
- 写代码
class Api::V1::TagsController < ApplicationController
def index
current_user = User.find request.env['current_user_id']
return render status: :not_found if current_user.nil?
tags = Tag.where(user_id: current_user).page(params[:page])
render json: { resources: tags, pager: {
page: params[:page] || 1,
per_page: Tag.default_per_page,
count: Tag.count
}}
end
end
- 写文档
require 'rails_helper'
require 'rspec_api_documentation/dsl'
resource "标签" do
get "/api/v1/tags" do
authentication :basic, :auth
parameter :page, '页码'
with_options :scope => :resources do
response_field :id, 'ID'
response_field :name, '名称'
response_field :sign, '符号'
response_field :user_id, '用户ID'
response_field :deleted_at, '删除时间'
end
let(:current_user) { User.create email: '1@qq.com' }
let(:auth) { "Bearer #{current_user.generate_jwt}" }
example "获取标签列表" do
11.times do Tag.create name: 'x', sign: 'x', user_id: current_user.id end
do_request
expect(status).to eq 200
json = JSON.parse response_body
expect(json['resources'].size).to eq 10
end
end
end
快速实现创建API
success post /api/v1/tags
由于tags的controller和model已经在第一个API内创建好了,无需创建,省略1,2两步。
- 创建model
运行db:migrate
- 创建controller
- 写测试
require 'rails_helper'
RSpec.describe "Api::V1::Tags", type: :request do
describe "创建标签" do
it '未登录创建标签' do
post '/api/v1/tags', params: { name: 'x', sign: 'x' }
expect(response).to have_http_status(401)
end
it '登录后创建标签' do
user = User.create email: '1@qq.com'
post '/api/v1/tags', params: { name: 'name', sign: 'sign' }, headers: user.generate_auth_header
expect(response).to have_http_status(200)
json = JSON.parse response.body
expect(json['resource']['name']).to eq 'name'
expect(json['resource']['sign']).to eq 'sign'
end
it '登录后创建标签失败,因为没有填写name' do
user = User.create email: '1@qq.com'
post '/api/v1/tags/', params: { sign:'sign' }, headers: user.generate_auth_header
expect(response).to have_http_status 422
json = JSON.parse response.body
expect(json['errors']['name'][0]).to eq "can't be blank"
end
it '登录后创建标签失败,因为没有填写sign' do
user = User.create email: '1@qq.com'
post '/api/v1/tags/', params: { name:'name' }, headers: user.generate_auth_header
expect(response).to have_http_status 422
json = JSON.parse response.body
expect(json['errors']['sign'][0]).to eq "can't be blank"
end
end
end
- 写代码
由于直接Tag.create 不传参数的时候数据库会报错,分部进行——先创建,后保存。成功时候返回200,ruby中代替为:ok
;失败的时候返回status状态码为422,ruby中代替为:unprocessable_entity
(即不可创建的实体,详细见MDN)。
class Api::V1::TagsController < ApplicationController
def create
current_user = User.find request.env['current_user_id']
return render status: 401 if current_user.nil?
tag = Tag.new name: params[:name], sign: params[:sign], user_id: current_user.id
# 由于直接Tag.create 不传参数的时候数据库会报错,分部进行——先创建,后保存
if tag.save
render json: { resource: tag }, status: :ok
else
render json: { errors: tag.errors }, status: :unprocessable_entity
end
end
end
由于tag的两个参数必须是必填项则需要在models层修改为必填项。
class Tag < ApplicationRecord
validates :name, presence: true
validates :sign, presence: true
belongs_to :user
end
- 写文档
require 'rails_helper'
require 'rspec_api_documentation/dsl'
resource "标签" do
authentication :basic, :auth
let(:current_user) { User.create email: '1@qq.com' }
let(:auth) { "Bearer #{current_user.generate_jwt}" }
post "/api/v1/tags" do
parameter :name, '名称', required: true
parameter :sign, '符号', required: true
with_options :scope => :resources do
response_field :id, 'ID'
response_field :name, '名称'
response_field :sign, '符号'
response_field :user_id, '用户ID'
response_field :deleted_at, '删除时间'
end
let (:name) { 'name' }
let (:sign) { 'sign' }
example "创建标签" do
do_request
expect(status).to eq 200
json = JSON.parse response_body
expect(json['resource']['name']).to eq 'name'
expect(json['resource']['sign']).to eq 'sign'
end
end
end
快速实现更新API
color2 patch /api/v1/tags/:id
由于tags的controller和model已经在第一个API内创建好了,无需创建,省略1,2两步。
- 创建model
运行db:migrate
- 创建controller
- 写测试
require 'rails_helper'
RSpec.describe "Api::V1::Tags", type: :request do
describe "更新标签" do
it '未登录修改标签' do
user = User.create email: '1@qq.com'
tag = Tag.create name:'x', sign:'x', user_id: user.id
patch "/api/v1/tags/#{tag.id}", params: { name: 'x', sign: 'y' }
expect(response).to have_http_status(401)
end
it '登录后修改标签' do
user = User.create email: '1@qq.com'
tag = Tag.create name:'x', sign:'x', user_id: user.id
patch "/api/v1/tags/#{tag.id}", params: { name: 'y', sign: 'y' }, headers: user.generate_auth_header
expect(response).to have_http_status(200)
json = JSON.parse response.body
expect(json['resource']['name']).to eq 'y'
expect(json['resource']['sign']).to eq 'y'
end
it '登录后部分修改标签' do
user = User.create email: '1@qq.com'
tag = Tag.create name:'x', sign:'x', user_id: user.id
patch "/api/v1/tags/#{tag.id}", params: { name: 'y' }, headers: user.generate_auth_header
expect(response).to have_http_status(200)
json = JSON.parse response.body
expect(json['resource']['name']).to eq 'y'
expect(json['resource']['sign']).to eq 'x'
end
end
end
- 写代码
find
方法是会报错的,具体详情可以查看链接。
ruby有一个permit
方法可以取非空的值,他会返回一个Hash值。
**errors.nil?**
只判空,**errors.empty?**
还会去查看数组长度是否为0。
class Api::V1::TagsController < ApplicationController
def update
# find 如果找不到是会报错的
tag = Tag.find params[:id]
# permit即在参数查找name和sign的key和值返回一个哈希值
tag.update params.permit(:name, :sign)
# 如果tag能够查找到且能够更新则返回tag,反正返回错误
if tag.errors.empty?
render json: { resource: tag }
else
render json: { errors: tag.errors }, status: :unprocessable_entity
end
end
end
- 写文档
require "rails_helper"
require "rspec_api_documentation/dsl"
resource "标签" do
authentication :basic, :auth
let(:current_user) { User.create email: "1@qq.com" }
let(:auth) { "Bearer #{current_user.generate_jwt}" }
patch "/api/v1/tags/:id" do
let (:tag) { Tag.create name: "x", sign: "x", user_id: current_user.id }
let (:id) { tag.id }
parameter :name, "名称"
parameter :sign, "符号"
with_options :scope => :resource do
response_field :id, "ID"
response_field :name, "名称"
response_field :sign, "符号"
response_field :user_id, "用户ID"
response_field :deleted_at, "删除时间"
end
let (:name) { "y" }
let (:sign) { "y" }
example "修改标签" do
do_request
expect(status).to eq 200
json = JSON.parse response_body
expect(json["resource"]["name"]).to eq name
expect(json["resource"]["sign"]).to eq sign
end
end
end
快速实现删除API
danger delete /api/v1/tags/:id
由于tags的controller和model已经在第一个API内创建好了,无需创建,省略1,2两步。
- 创建model
运行db:migrate
- 创建controller
- 写测试
删除后要重新获取对象的信息可以使用reload
。状态码403 Forbidden
代表客户端错误,服务器有能力处理该请求,但是拒绝授权访问,具体内容可以查看MDN。
require 'rails_helper'
RSpec.describe "Api::V1::Tags", type: :request do
describe "删除标签" do
it "未登录删除标签" do
user = User.create email: '1@qq.com'
tag = Tag.create name: 'x', sign: 'x', user_id: user.id
delete "/api/v1/tags/#{tag.id}"
expect(response).to have_http_status 401
end
it "登录后删除标签" do
user = User.create email: '1@qq.com'
tag = Tag.create name: 'x', sign: 'x', user_id: user.id
delete "/api/v1/tags/#{tag.id}", headers: user.generate_auth_header
expect(response).to have_http_status 200
# 删除后要重新获取一下数据库的信息
# tag = Tag.find tag.id
tag.reload
expect(tag.deleted_at).not_to eq nil
end
it "登录后删除别人的标签" do
user = User.create email: '1@qq.com'
other = User.create email: '2@qq.com'
tag = Tag.create name: 'x', sign: 'x', user_id: other.id
delete "/api/v1/tags/#{tag.id}", headers: user.generate_auth_header
expect(response).to have_http_status 403
end
end
end
- 写代码
这里的删除是软删除,软删除又叫逻辑删除,在数据库中设置一个字段来表示删除状态;硬删除才是传统的物理删除,真正的从数据库中删除。如下代码段中软删除只是更新了tag
的delete_at
的字段。:forbidden
表示状态码403 Forbidden
。
class Api::V1::TagsController < ApplicationController
def destroy
tag = Tag.find params[:id]
return head :forbidden unless tag.user_id == request.env['current_user_id']
tag.deleted_at = Time.now
if tag.save
head 200
else
render json: { errors: tag.errors }, status: :unprocessable_entity
end
end
end
- 写文档
require "rails_helper"
require "rspec_api_documentation/dsl"
resource "标签" do
authentication :basic, :auth
let(:current_user) { User.create email: "1@qq.com" }
let(:auth) { "Bearer #{current_user.generate_jwt}" }
delete "/api/v1/tags/:id" do
let (:tag) { Tag.create name: "x", sign: "x", user_id: current_user.id }
let (:id) { tag.id }
example "删除标签" do
do_request
expect(status).to eq 200
end
end
end
快速实现单个读取API
color1 get /api/v1/tags/:id
由于tags的controller和model已经在第一个API内创建好了,无需创建,省略1,2两步。
- 创建model
运行db:migrate
- 创建controller
- 写测试
require 'rails_helper'
RSpec.describe "Api::V1::Tags", type: :request do
describe "获取标签" do
it "未登录获取标签" do
user = User.create email: '1@qq.com'
tag = Tag.create name: 'x', sign: 'x', user_id: user.id
get "/api/v1/tags/#{tag.id}"
expect(response).to have_http_status 401
end
it "登录后获取标签" do
user = User.create email: '1@qq.com'
tag = Tag.create name: 'tag1', sign: 'x', user_id: user.id
get "/api/v1/tags/#{tag.id}", headers: user.generate_auth_header
expect(response).to have_http_status 200
json = JSON.parse response.body
expect(json['resource']['id']).to eq tag.id
end
it "登录后获取不属于自己的标签" do
user = User.create email: '1@qq.com'
another_user = User.create email: '2@qq.com'
tag = Tag.create name: 'tag1', sign: 'x', user_id: another_user.id
get "/api/v1/tags/#{tag.id}", headers: user.generate_auth_header
expect(response).to have_http_status 403
end
end
end
- 写代码
class Api::V1::TagsController < ApplicationController
def show
tag = Tag.find params[:id]
return head :forbidden unless tag.user_id == request.env['current_user_id']
render json: { resource: tag }
end
end
- 写文档
require "rails_helper"
require "rspec_api_documentation/dsl"
resource "标签" do
authentication :basic, :auth
let(:current_user) { User.create email: "1@qq.com" }
let(:auth) { "Bearer #{current_user.generate_jwt}" }
get "/api/v1/tags/:id" do
let (:tag) { Tag.create name: "x", sign: "x", user_id: current_user.id }
let (:id) { tag.id }
with_options :scope => :resources do
response_field :id, "ID"
response_field :name, "名称"
response_field :sign, "符号"
response_field :user_id, "用户ID"
response_field :deleted_at, "删除时间"
end
example "获取标签" do
do_request
expect(status).to eq 200
json = JSON.parse response_body
expect(json['resource']['id']).to eq tag.id
end
end
end