【问题标题】:Fixing Rails Controller固定导轨控制器
【发布时间】:2020-03-03 20:00:11
【问题描述】:

我正在尝试编写一个简单的 Rails API。在我的应用程序逻辑中,用户模型“has_many”预算和预算模型(自然)“belongs_to”用户。我正在尝试设置一个简单的身份验证系统以用于反应前端。在测试请求的过程中,我偶然发现了一些错误,这些错误似乎来自我的控制器。相关数据如下:

预算控制器

class Api::V1::BudgetsController < ApplicationController

  def index
    @budgets = Budget.all
    render json: @budgets
  end

  def show
    @budget = Budget.find(params[:id])
    render json: @budget
  end

  def create
    @budget = current_user.budgets.build(budget_params)
    if @budget.save
      render json: @budget, status: 201
    else
      render json: { errors: @budget.errors.full_messages }, status: 422
    end
  end

  def destroy
    @budget = Budget.find(params[:id])
    @budget.destroy
  end

  private

  def budget_params
    params.require(:budget).permit(:start_date, :end_date, :income)
  end
end

会话控制器

class Api::V1::SessionsController < ApplicationController

  def create
    user = User.find_by(email: session_params[:email])
    if user
      log_in user
      render json: {
        logged_in: true,
        user
      }
    else
      render json: {
        status: 401,
        error: 'Please try again'
      }
    end
  end

  def destroy
    log_out if logged_in?
  end

  private

  def session_params
    params.require(:user).permit(:name, :email)
  end
end

会话助手

module SessionsHelper

  def log_in(user)
    session[user_id] = user.id
  end

  def current_user
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
  end

  def logged_in?
    !current_user.nil?
  end

  def log_out
    session.clear
    @current_user = nil
  end
end

请求 RSpec 测试

require 'rails_helper'

RSpec.describe 'Budgets API', type: :request do
  let!(:user) { create(:user) }
  let!(:budgets) { create_list(:budget, 5, user_id: user.id) }
  let(:budget_id) { budgets.first.id }
describe 'POST /api/v1/budgets' do
    let(:valid_attributes) do
      {
        start_date: '2020-03-01',
        end_date: '2020-03-31',
        income: 7000
      }
    end

    let(:invalid_attributes) do
      {
        start_date: '',
        end_date: '',
        income: ''
      }
    end

    context 'when the request is valid' do
      before(:each) { post '/api/v1/budgets', params: { budget: valid_attributes } }

      it 'creates a budget' do
        expect(json['start_date']).to eq('2020-03-01')
      end

      it 'returns status code 201' do
        expect(response).to have_http_status(201)
      end
    end

    context 'when the request is invalid' do
      before(:each) { post '/api/v1/budgets', params: { budget: invalid_attributes } }

      it 'returns status code 422' do
        expect(response).to have_http_status(422)
      end
    end
  end
end

我得到的错误如下:

Failures:

  1) Budgets API POST /api/v1/budgets when the request is valid creates a budget
     Failure/Error: @budget = current_user.budgets.build(budget_params)

     NoMethodError:
       undefined method `budgets' for nil:NilClass
     # ./app/controllers/api/v1/budgets_controller.rb:14:in `create'
     # ./spec/requests/budgets_spec.rb:62:in `block (4 levels) in <main>'
     # ./spec/rails_helper.rb:73:in `block (3 levels) in <top (required)>'
     # ./spec/rails_helper.rb:72:in `block (2 levels) in <top (required)>'

  2) Budgets API POST /api/v1/budgets when the request is valid returns status code 201
     Failure/Error: @budget = current_user.budgets.build(budget_params)

     NoMethodError:
       undefined method `budgets' for nil:NilClass
     # ./app/controllers/api/v1/budgets_controller.rb:14:in `create'
     # ./spec/requests/budgets_spec.rb:62:in `block (4 levels) in <main>'
     # ./spec/rails_helper.rb:73:in `block (3 levels) in <top (required)>'
     # ./spec/rails_helper.rb:72:in `block (2 levels) in <top (required)>'

  3) Budgets API POST /api/v1/budgets when the request is invalid returns status code 422
     Failure/Error: @budget = current_user.budgets.build(budget_params)

     NoMethodError:
       undefined method `budgets' for nil:NilClass
     # ./app/controllers/api/v1/budgets_controller.rb:14:in `create'
     # ./spec/requests/budgets_spec.rb:74:in `block (4 levels) in <main>'
     # ./spec/rails_helper.rb:73:in `block (3 levels) in <top (required)>'
     # ./spec/rails_helper.rb:72:in `block (2 levels) in <top (required)>'

任何帮助将不胜感激。

【问题讨论】:

  • SessionsController 和 SessionsHelper 似乎与 BudgetsController 没有关系。你也在用设计吗? ApplicationController 中是否还有更多代码?
  • SessionsHelper 模块包含在 ApplicationController 中

标签: ruby-on-rails ruby rspec


【解决方案1】:

current_usernil,因为测试从未登录用户。仅仅创建用户是不够的,他们必须登录。

通常您会在 before 挂钩中执行此操作。

RSpec.describe 'Budgets API', type: :request do
  let!(:user) { create(:user) }
  before {
    post("/api/v1/sessions",
      params: {
        user: { email: user.email }
      }
    )
  }

知道用户电子邮件地址的任何人都可以访问您的 API。我建议不要使用自己的身份验证,而是使用预先存在的 gem,例如 Devise

【讨论】:

  • 由于某种原因,在使用 devise 时,我仍然会出错。此外,它是针对学校项目的,因此 lvl 的身份验证并不是真正必要的。我尝试了您最初的建议,但现在出现此错误: ActionController::ParameterMissing: param is missing or the value is empty: user
  • @Genetic1989 如果您想使用current_userbefore { sign_in user },Devise 还要求您登录用户。你的session_params 需要一个用户,所以我猜那是params: { user: { email: user.email } }。你也允许一个名字,但不要对它做任何事情。学校项目是用来学习的,正确的学习最好,用gem少出错。
  • 确实如此。我实现了 Devise,但测试失败,理由是“预计响应的状态代码为 201,但它是 302”。我使用了设计方法“before_action:authenticate_user!”在预算和用户控制器中,但无济于事。
  • @Genetic1989 如果您没有登录用户,这是正确的行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-23
  • 1970-01-01
  • 1970-01-01
  • 2014-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多