为什么 Rails RSpec 响应显示 302 而不是 401?

问题描述 投票:0回答:4

我已经被这个问题困扰好几天了,我不知道它出了什么问题。几个月前我开始使用 Ruby on Rails,目前正在学习使用 API 进行身份验证。我看过其他类似的主题这里那里,但没有一个有帮助。

我的问题是每当我在此代码上运行 RSpec(位于

spec/api/v1/controllers/posts_controller_spec.rb

require 'rails_helper'

RSpec.describe Api::V1::PostsController, type: :controller do
  let(:my_user) { create(:user) }
  let(:my_topic) { create(:topic) }
  let(:my_post) { create(:post, topic: my_topic, user: my_user) }

  context "unauthenticated user" do

    it "PUT update returns http unauthenticated" do
      put :update, topic_id: my_topic.id, id: my_post.id, post: {title: my_post.title, body: my_post.body}
      expect(response).to have_http_status(401)
    end
    ...

我不断得到

...
spec/api/v1/controllers/posts_controller_spec.rb -e "unauthenticated user"
Run options: include {:full_description=>/unauthenticated\ user/}
FFF

Failures:

  1) PostsController unauthenticated user PUT update returns http unauthenticated
     Failure/Error: expect(response).to have_http_status(401)
       expected the response to have status code 401 but it was 302
     # ./spec/api/v1/controllers/posts_controller_spec.rb:12:in `block (3 levels) in <top (required)>'

如果这有帮助,我的控制器

app/controllers/api/v1/posts_controller_spec.rb

class Api::V1::PostsController < Api::V1::BaseController
  before_action :authenticate_user, except: [:index, :show]
  before_action :authorize_user, except: [:index, :show]


     def update
       post = Post.find(params[:id])

       if post.update_attributes(post_params)
         render json: post.to_json, status: 200
       else
         render json: {error: "Post update failed", status: 400}, status: 400
       end
     end
    ...

我的基地控制器

class Api::V1::BaseController < ApplicationController

  skip_before_action :verify_authenticity_token

  rescue_from ActiveRecord::RecordNotFound, with: :not_found
  rescue_from ActionController::ParameterMissing, with: :malformed_request

  def authenticate_user
    authenticate_or_request_with_http_token do |token, options|
      @current_user = User.find_by(auth_token: token)
    end
  end

  def authorize_user
    unless @current_user && @current_user.admin?
      render json: {error: "Not Authorized", status: 403}, status: 403
    end
  end

  def malformed_request
    render json: {error: "The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.", status: 400}, status: 400
  end

  def not_found
    render json: {error: "Record not found", status: 404}, status: 404
  end
end

最后,routes.rb 显示:

  namespace :api do
   namespace :v1 do
     resources :users, only: [:index, :show, :create, :update]
     resources :topics, except: [:edit, :new] do
       resources :posts, only: [:update, :create, :destroy]
     end
   end
 end

我有一种强烈的预感,我的 before_action 是罪魁祸首,但我无法确定我做错了什么。我使用类似的 RSpec 测试对主题(帖子嵌套在主题中)做了类似的事情,我的主题 RSpec 通过了,而我的帖子 RSpec 惨败。

有人可以帮我指出我做错了什么,以及如何通过 RSpec 测试而不显示 302 状态代码?

另外,为什么显示 302 状态代码?

ruby-on-rails rspec http-status-code-302 rails-api
4个回答
3
投票

问题是你正在使用

before_action :authenticate_user, except: [:index, :show]
,大概是Devise的帮手吧?如果是这样,当您尝试在未登录的情况下访问资源时,此帮助程序会将您“重定向”到登录页面,这是您在使用浏览器时期望发生的情况,但您的情况并非如此。您可以找到一些替代方案,覆盖 Devise 行为,我的解决方案是定义我的 authenticate_user! 方法,如果用户未登录,则返回 401,使用另一个 devise 帮助器方法,如下所示:
def authenticate_user!
  if user_signed_in?
    @user = current_user
  else
    head :unauthorized
  end 
end 

这是对我有用的情况,你的可能会有所不同。


1
投票

let(:auth_headers) { Devise::JWT::TestHelpers.auth_headers({ 'ACCEPT': 'application/json' }, user) } # ... get "/api/v1/something", headers: auth_headers

在我的请求规格中。

我首先尝试通过删除

auth_headers

来测试 401 Unauthorized 情况,然后导致意外的

302 Found
响应。 但是,通过 Postman 测试我的 API 按预期工作(没有 auth 标头的 401 响应)。

解决方案是删除

Authorization

标头并保留

Accept
标头,这样应用程序就会知道它没有与用户通信,因此不会重定向到登录页面: let(:auth_headers) { Devise::JWT::TestHelpers.auth_headers({ 'ACCEPT': 'application/json' }, user) } # ... get "/api/v1/something", headers: { 'ACCEPT': 'application/json' }
    

我认为问题在于,如果请求是从浏览器发出的,devise 将返回 HTTP 302 Redirect,否则返回 401 Unauthorized。

1
投票

put :update, topic_id: my_topic.id, id: my_post.id, post: {title: my_post.title, body: my_post.body}, as: :json

感谢@LondonAppDev,添加为::json,也为我的测试提供了技巧

0
投票

© www.soinside.com 2019 - 2024. All rights reserved.