- 编写用户控制器相关的用户行为;
- 添加路允许通过路由控制用户行为;
- 编写用户行为的单元测试,完成相关测试。
上一节我们已经实现了用户模型的验证并实现密码加密。本节我们将继续开发用户相关功能,最终目标是基本实现用户的管理功能。
- 查看用户列表;
- 增加一个用户;
- 查看指定用户详情
- 修改某个用户信息
- 删除某个用户;
- index: 返回用户列表
- create: 新增用户
- show: 返回指定的用户信息
- update:修改指定用户
- destroy: 删除指定用户
-
使用命令创建控制器
$ rails generate controller 复数控制器名
该命令会生成控制器文件,以及控制器的测试文件。
-
控制器单元测试的基本语法
test '测试描述:不允许重复' do 模型对象 = 模型.new(字段1:值, ...) # assert表示断言通过验证 assert 模型对象.valid? # assert_not 表示断言没有通过验证 assert_not 模型对象.valid? end
-
控制器如何获取参数
params[:参数符号]
-
控制器如何返回json数据
render json: 数据
-
控制器返回数据指定状态码
render status: http状态码
-
常用的http状态码
请求方法 状态码 意义 message Rails内置符号 get 200 请求成功 ok :ok post 201 创建成功 ok :created put/patch 202 修改成功 ok delete 204 删除成功 ok :no_content get 404 未找到资源 unfound :not_found all 401 token过期或失效 token expired or invalid :unauthorized all 403 权限不够 forbidden :forbidden all 405 请求参数有问题 params error all 500 系统错误 server error :internal_server_error get 301 重定向 :moved_permanently get 304 没有变动 :not_modified Rails支持的所有的状态码以及标识:
-
Response Class HTTP Status Code Symbol Informational 100 :continue 101 :switching_protocols 102 :processing Success 200 :ok 201 :created 202 :accepted 203 :non_authoritative_information 204 :no_content 205 :reset_content 206 :partial_content 207 :multi_status 208 :already_reported 226 :im_used Redirection 300 :multiple_choices 301 :moved_permanently 302 :found 303 :see_other 304 :not_modified 305 :use_proxy 307 :temporary_redirect 308 :permanent_redirect Client Error 400 :bad_request 401 :unauthorized 402 :payment_required 403 :forbidden 404 :not_found 405 :method_not_allowed 406 :not_acceptable 407 :proxy_authentication_required 408 :request_timeout 409 :conflict 410 :gone 411 :length_required 412 :precondition_failed 413 :payload_too_large 414 :uri_too_long 415 :unsupported_media_type 416 :range_not_satisfiable 417 :expectation_failed 421 :misdirected_request 422 :unprocessable_entity 423 :locked 424 :failed_dependency 426 :upgrade_required 428 :precondition_required 429 :too_many_requests 431 :request_header_fields_too_large 451 :unavailable_for_legal_reasons Server Error 500 :internal_server_error 501 :not_implemented 502 :bad_gateway 503 :service_unavailable 504 :gateway_timeout 505 :http_version_not_supported 506 :variant_also_negotiates 507 :insufficient_storage 508 :loop_detected 510 :not_extended 511 :network_authentication_required -
返回数据格式
return json: {error_code:错误码, data:数据, message:"信息"}, status:http状态码 错误码代表发生的错误编号,代表了业务是否成功,0为没有发生错误! http状态码只负责说明本次http请求是否请求成功!不代表业务本身是否成功!
$ rails generate controller api::v1::users
Running via Spring preloader in process 7751
create app/controllers/api/v1/users_controller.rb
invoke test_unit
create test/controllers/api/v1/users_controller_test.rb
config/routes.rb
Rails.application.routes.draw do
namespace :api, defaults: { format: :json } do
namespace :v1 do
# 编写路由
resources :users, only: [:index, :show, :create, :update, :destroy]
end
end
end
only 是限制当前只开放对这些方法的路由
$ rails routes -c users
--[ Route 1 ]-----------------------------------------------------
Prefix | api_v1_users
Verb | GET
URI | /api/v1/users(.:format)
Controller#Action | api/v1/users#index {:format=>:json}
--[ Route 2 ]-----------------------------------------------------
Prefix |
Verb | POST
URI | /api/v1/users(.:format)
Controller#Action | api/v1/users#create {:format=>:json}
--[ Route 3 ]-----------------------------------------------------
Prefix | api_v1_user
Verb | GET
URI | /api/v1/users/:id(.:format)
Controller#Action | api/v1/users#show {:format=>:json}
--[ Route 4 ]-----------------------------------------------------
Prefix |
Verb | PATCH
URI | /api/v1/users/:id(.:format)
Controller#Action | api/v1/users#update {:format=>:json}
--[ Route 5 ]-------------------------------------------------------
Prefix |
Verb | PUT
URI | /api/v1/users/:id(.:format)
Controller#Action | api/v1/users#update {:format=>:json}
--[ Route 6 ]-------------------------------------------------------
Prefix |
Verb | DELETE
URI | /api/v1/users/:id(.:format)
Controller#Action | api/v1/users#destroy {:format=>:json}
思路解析
获取用户列表,可以直接使用User模型相关方法获取,这里要注意分页和排序的情况。
路由是: api/v1/users?page=1&per_page=10
返回值部分我们采用json数据类型的返回值,状态码就按照上面我们规划表格中的状态码:200。
至于测试部分,我们应该测试两部分
- 状态码应该是200
- 返回值的用户列表,但是有可能是空的,这个我们暂不测试
test/controllers/api/v1/users_controller_test.rb
require "test_helper"
class Api::V1::UsersControllerTest < ActionDispatch::IntegrationTest
test "index_success: should show users" do
get api_v1_users_path, as: :json
assert_response 200
end
end
运行测试会显示一个失败,原因是找不到 index
方法, 这是当然的,我们还没有定义 index
方法。
$ rails test
# Running:
.......E
Error:
Api::V1::UsersControllerTest#test_index_success:_should_show_users:
DRb::DRbRemoteError: The action 'index' could not be found for Api::V1::UsersController (AbstractController::ActionNotFound)
test/controllers/api/v1/users_controller_test.rb:5:in `block in <class:UsersControllerTest>'
app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < ApplicationController
before_action :set_per_page, only: [:index]
before_action :set_page, only: [:index]
def index
@users = User.offset(@page).limit(@per_page)
render json: {error_code:0, data:@users, message:'ok'}, status: 200
end
private
def _to_i(param, default_no = 1)
param && param&.to_i > 0 ? param&.to_i : default_no.to_i
end
# api/v1/users?page=1
def set_page
@page = _to_i(params[:page], 1)
@page = set_per_page * (@page - 1)
end
# api/v1/users?per_page=10
def set_per_page
@per_page = _to_i(params[:per_page], 10)
end
end
再次运行测试,这次测试通过了。
$ rails test
# Running:
........
Finished in 0.533797s, 14.9870 runs/s, 14.9870 assertions/s.
8 runs, 8 assertions, 0 failures, 0 errors, 0 skips
$ git add .
$ git commit -m "add users_controller index action"
思路解析
获取指定用户,路由是:get:api/v1/users/:id
。
我们首先要获取参数中的用户id,然后根据用户id找到具体的用户,返回用户信息,状态码仍是200。
至于测试部分,我们应该测试两部分
- 状态码应该是200
- 返回值的用户就是我们指定的用户信息。返回值数据类型: {email:'[email protected]', password_digest:'11111', role:1}
test/controllers/api/v1/users_controller_test.rb
class Api::V1::UsersControllerTest < ActionDispatch::IntegrationTest
setup do
@user = users(:one)
end
# ... ...
test "show_success: should show user" do
get api_v1_user_path(@user), as: :json
json_response = JSON.parse(self.response.body)
# 验证状态码
assert_response 200
# 验证返回数据
assert_equal @user.email, json_response['data']['email']
end
end
运行测试会显示一个失败,原因是找不到 show
方法, 这是当然的,我们还没有定义 show
方法。
$ rails test
Error:
Api::V1::UsersControllerTest#test_show_success:_should_show_user:
DRb::DRbRemoteError: The action 'show' could not be found for Api::V1::UsersController
Did you mean? index (AbstractController::ActionNotFound)
test/controllers/api/v1/users_controller_test.rb:14:in `block in <class:UsersControllerTest>'
app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < ApplicationController
before_action :set_user, only: [:show]
def show
render json: {error_code:0, data:@user, message:'ok'}, status: 200
end
private
#......
def set_user
@user = User.find_by_id params[:id].to_i
@user = @user || {}
end
end
再次运行测试,这次测试通过了。
$ rails test
Finished in 0.207461s, 43.3816 runs/s, 48.2018 assertions/s.
9 runs, 10 assertions, 0 failures, 0 errors, 0 skips
$ git add .
$ git commit -m "add users_controller show action"
思路解析
新建用户,我们的路由是:post:api/v1/users
。
我们的参数应该类似: {user:{email:'[email protected]', password:'123456', role:1}}
首先要创建一个用户对象来接受参数,然后保存!
如果保存成功,则返回成功的信息;否则,返回失败的信息。
至于测试部分,我们应该测试两部分
- 状态码应该是
201
- 添加成功后用户的总数增加 1。
test/controllers/api/v1/users_controller_test.rb
class Api::V1::UsersControllerTest < ActionDispatch::IntegrationTest
test "create_success: should create user" do
# 验证某个值变化了
assert_difference('User.count', 1) do
post api_v1_users_path,
params: {user:{email: '[email protected]', password: '123456'}},
as: :json
end
assert_response 201
end
end
运行测试会显示一个失败,原因是找不到 create
方法, 这是当然的,我们还没有定义 create
方法。
$ rails test
Error:
Api::V1::UsersControllerTest#test_create_success:_should_create_user:
DRb::DRbRemoteError: The action 'create' could not be found for Api::V1::UsersController
app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < ApplicationController
def create
@user = User.new(user_params)
if @user.save
render json: {error_code:0, data:@user, message:'ok'}, status: 201
else
render json: {error_code:500, message:@user.errors}, status: 201
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password)
end
end
再次运行测试,这次测试通过了。
$ rails test
Finished in 0.207461s, 43.3816 runs/s, 48.2018 assertions/s.
9 runs, 10 assertions, 0 failures, 0 errors, 0 skips
思路解析
修改用户,路由是:put:api/v1/users/:id
。
我们的参数应该类似: {user:{email:'[email protected]', password:'123456', role:1}}
首先要利用用户id找到对应用户,然后把接受到的用户信息赋值给该用户并保存,完成修改!
如果保存成功,则返回成功的信息;否则,返回失败的信息。
至于测试部分,我们应该测试状态码即可
- 状态码应该是 202
test/controllers/api/v1/users_controller_test.rb
class Api::V1::UsersControllerTest < ActionDispatch::IntegrationTest
test "update_success: should update user" do
put api_v1_user_path(@user),
params: {user:{email: '[email protected]', password: '123456'}},
as: :json
assert_response 202
end
end
运行测试会显示一个失败,原因是找不到 create
方法, 这是当然的,我们还没有定义 show
方法。
$ rails test
Error:
Api::V1::UsersControllerTest#test_update_success:_should_update_user:
DRb::DRbRemoteError: The action 'update' could not be found for Api::V1::UsersController
app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < ApplicationController
before_action :set_user, only: [:show, :update]
def update
if @user.update(user_params)
render json: {error_code:0, data:@user, message:'ok'}, status: 202
else
render json: {error_code:500, message:@user.errors}, status: 202
end
end
end
再次运行测试,这次测试通过了。
$ rails test
Finished in 0.413498s, 26.6023 runs/s, 31.4391 assertions/s.
11 runs, 13 assertions, 0 failures, 0 errors, 0 skips
思路解析
删除用户,我们的路由是:delete:api/v1/users/:id
。
首先要利用用户id找到对应用户,然后直接删除即可!
如果删除成功,则返回成功的信息;否则,返回失败的信息。
至于测试部分,我们应该测试两部分
- 状态码应该是 204
- 删除成功后用户的总数减少1。
test/controllers/api/v1/users_controller_test.rb
class Api::V1::UsersControllerTest < ActionDispatch::IntegrationTest
test "destroy_success: should destroy user" do
# 验证某个值变化了
assert_difference('User.count', -1) do
delete api_v1_user_path(@user),
as: :json
end
assert_response 204
end
end
运行测试会显示一个失败,原因是找不到 create
方法, 这是当然的,我们还没有定义 show
方法。
$ rails test
Error:
Api::V1::UsersControllerTest#test_destroy_success:_should_destroy_user:
DRb::DRbRemoteError: The action 'destroy' could not be found for Api::V1::UsersController
app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < ApplicationController
before_action :set_user, only: [:show, :update, :destroy]
def destroy
@user.destroy
render json: {error_code:0, message:'ok'}, status: 204
end
end
再次运行测试,这次测试通过了。
$ rails test
Finished in 0.408386s, 29.3840 runs/s, 36.7299 assertions/s.
12 runs, 15 assertions, 0 failures, 0 errors, 0 skips
$ git add .
$ git commit -m "set users controller and routes"
$ git checkout master
$ git merge chapter04
我们本节完成了用户控制器的相关方法,并设置了对应路由提供用户在外部直接访问。但就我们今天实现的功能,实际上有一个巨大的隐患,比如最敏感的删除操作,应该是具有一定权限的人才能有的操作,比如管理员或用户自己,但本节我们实现的这个功能没有做任何约束,这在具有一定权限的系统中是不被允许的。但是要实现用户的权限控制,首先要求就是用户是登录的!然后我们获取登录用户的信息,借以实现对登录用户的控制。在下一节,我们将实现用户的状态管理,然后我们就能实现对用户的权限控制了。
革命刚刚开始,同志们一定要加油!一定要跟上脚步,认真练习!