Skip to content

Latest commit

 

History

History
716 lines (492 loc) · 21.5 KB

05 用户模块- 控制器相关功能开发.md

File metadata and controls

716 lines (492 loc) · 21.5 KB

使用rails6 开发纯后端 API 项目

用户模块: 控制器


本节目标
  • 编写用户控制器相关的用户行为;
  • 添加路允许通过路由控制用户行为;
  • 编写用户行为的单元测试,完成相关测试。

上一节我们已经实现了用户模型的验证并实现密码加密。本节我们将继续开发用户相关功能,最终目标是基本实现用户的管理功能。

1. 功能分析

1.1 用户模块要实现的功能
  • 查看用户列表;
  • 增加一个用户;
  • 查看指定用户详情
  • 修改某个用户信息
  • 删除某个用户;
1.2 要实现的方法
  • index: 返回用户列表
  • create: 新增用户
  • show: 返回指定的用户信息
  • update:修改指定用户
  • destroy: 删除指定用户

2. 控制器相关功能开发

2.1 知识点预热

2.1.1 Rails 相关
  • 使用命令创建控制器

    $ 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请求是否请求成功!不代表业务本身是否成功!

2.2 创建users控制器

$ 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

2.3 添加用户相关方法的路由

2.3.1 修改路由文件

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 是限制当前只开放对这些方法的路由

2.3.2 在Rails console 中可以查看 users控制器 的路由列表
$ 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}

2.4 用户列表:index 方法开发


思路解析

获取用户列表,可以直接使用User模型相关方法获取,这里要注意分页和排序的情况。

路由是: api/v1/users?page=1&per_page=10

返回值部分我们采用json数据类型的返回值,状态码就按照上面我们规划表格中的状态码:200。

至于测试部分,我们应该测试两部分

  • 状态码应该是200
  • 返回值的用户列表,但是有可能是空的,这个我们暂不测试

2.4.1 编写测试

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>'
2.4.2 定义users#index方法

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
2.4.3 代码变更提交
$ git add .
$ git commit -m "add users_controller index action"

2.5 用户详情:show 方法开发


思路解析

获取指定用户,路由是:get:api/v1/users/:id

我们首先要获取参数中的用户id,然后根据用户id找到具体的用户,返回用户信息,状态码仍是200。

至于测试部分,我们应该测试两部分

  • 状态码应该是200
  • 返回值的用户就是我们指定的用户信息。返回值数据类型: {email:'[email protected]', password_digest:'11111', role:1}

2.5.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>'
2.5.2 定义users#show方法

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
2.5.3 代码变更提交
$ git add .
$ git commit -m "add users_controller show action"

2.6 新建用户:create方法开发


思路解析

新建用户,我们的路由是:post:api/v1/users

我们的参数应该类似: {user:{email:'[email protected]', password:'123456', role:1}}

首先要创建一个用户对象来接受参数,然后保存!

如果保存成功,则返回成功的信息;否则,返回失败的信息。

至于测试部分,我们应该测试两部分

  • 状态码应该是 201
  • 添加成功后用户的总数增加 1。

2.5.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
2.5.2 定义users#create方法

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

2.7 修改用户:update方法


思路解析

修改用户,路由是:put:api/v1/users/:id

我们的参数应该类似: {user:{email:'[email protected]', password:'123456', role:1}}

首先要利用用户id找到对应用户,然后把接受到的用户信息赋值给该用户并保存,完成修改!

如果保存成功,则返回成功的信息;否则,返回失败的信息。

至于测试部分,我们应该测试状态码即可

  • 状态码应该是 202

2.7.1 编写测试

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
2.7.2 定义users#update方法

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

2.8 删除用户:destroy方法


思路解析

删除用户,我们的路由是:delete:api/v1/users/:id

首先要利用用户id找到对应用户,然后直接删除即可!

如果删除成功,则返回成功的信息;否则,返回失败的信息。

至于测试部分,我们应该测试两部分

  • 状态码应该是 204
  • 删除成功后用户的总数减少1。

2.8.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
2.7.2 定义users#destroy方法

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

2.8 文件版本管理

2.8.1 添加所有变动文件到git管理
$ git add .
2.8.2 提交所有变动文件
$ git commit -m "set users controller and routes"
2.8.3 合并版本到主分支
$ git checkout master
$ git merge chapter04

3. 总结

我们本节完成了用户控制器的相关方法,并设置了对应路由提供用户在外部直接访问。但就我们今天实现的功能,实际上有一个巨大的隐患,比如最敏感的删除操作,应该是具有一定权限的人才能有的操作,比如管理员或用户自己,但本节我们实现的这个功能没有做任何约束,这在具有一定权限的系统中是不被允许的。但是要实现用户的权限控制,首先要求就是用户是登录的!然后我们获取登录用户的信息,借以实现对登录用户的控制。在下一节,我们将实现用户的状态管理,然后我们就能实现对用户的权限控制了。

革命刚刚开始,同志们一定要加油!一定要跟上脚步,认真练习!