简体   繁体   English

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

[英]Why does Rails RSpec response show 302 instead of 401?

I have been stuck on this problem for several days and I don't know what is wrong with it.我已经被这个问题困扰了好几天了,我不知道它有什么问题。 I started Ruby on Rails few months ago and I am currently learning authentication with API.几个月前我开始使用 Ruby on Rails,目前正在学习使用 API 进行身份验证。 I have looked at other similar topics here and there but none of them helped.我在这里那里查看了其他类似的主题,但没有一个有帮助。

My problem is whenever I run RSpec on this code (located at spec/api/v1/controllers/posts_controller_spec.rb )我的问题是每当我在此代码上运行 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
    ...

I keep getting我不断得到

...
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)>'

If this helps, my controller, app/controllers/api/v1/posts_controller_spec.rb has如果这有帮助,我的控制器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
    ...

My base_controller我的 base_controller

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

Lastly, routes.rb shows:最后, 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

I have a strong hunch that my before_action is the culprit, but I can't pinpoint what I did wrong.我有一种强烈的预感,我的 before_action 是罪魁祸首,但我无法确定我做错了什么。 I have done similar thing on Topics (Posts are nested within Topics) using similar RSpec testing and my Topic RSpec passes, whereas my Posts RSpec fail miserably.我使用类似的 RSpec 测试在主题(帖子嵌套在主题中)上做了类似的事情,并且我的主题 RSpec 通过,而我的帖子 RSpec 失败了。

Can someone please help me to point out what I did wrong, and how can I pass the RSpec test and not show 302 status code?有人可以帮我指出我做错了什么,我怎样才能通过 RSpec 测试而不显示 302 状态码?

As an extra, why did it show 302 status code?另外,为什么显示302状态码?

The problem is that you're using问题是你正在使用

before_action :authenticate_user, except: [:index, :show] , probably the helper from Devise, correct? before_action :authenticate_user, except: [:index, :show] ,可能是 Devise 的助手,对吗? If so, when you try to access a resource without being logged in, this helper will redirect you to the login page, something that you expect to happen when you're using a browser, which is not your case.如果是这样,当您尝试在未登录的情况下访问资源时,此帮助程序会将您重定向到登录页面,这是您在使用浏览器时期望发生的事情,而您的情况并非如此。 You can find some alternatives, overwriting the Devise behavior, my solution was to define my authenticate_user!您可以找到一些替代方案,覆盖设计行为,我的解决方案是定义我的authenticate_user! method to return a 401 if the user was not signed in, using another devise helper method, something like this:如果用户未登录,则返回 401 的方法,使用另一个设计辅助方法,如下所示:

    def authenticate_user!
      if user_signed_in?
        @user = current_user
      else
        head :unauthorized
      end 
    end 

This is just the case that worked for me, yours may be different.这只是对我有用的情况,你的可能会有所不同。

I ran into to same issue, while normally using我遇到了同样的问题,而通常使用

let(:auth_headers) { Devise::JWT::TestHelpers.auth_headers({ 'ACCEPT': 'application/json' }, user) }

# ...


get "/api/v1/something", headers: auth_headers

in my request specs.在我的请求规格中。

I first tried to test the 401 Unauthorized case by just removing auth_headers , which then lead to the unexpected 302 Found response.我首先尝试通过删除auth_headers来测试401 Unauthorized案例,然后导致意外的302 Found响应。

However, testing my API via Postman worked as expected (401 response without auth header).但是,通过 Postman 测试我的 API 按预期工作(没有 auth 标头的 401 响应)。

The solution was to just remove the Authorization header and keeping the Accept header, so the application would know that it's not talking to a user and thus would not redirect to the login page:解决方案是仅删除Authorization标头并保留Accept标头,因此应用程序将知道它不是在与用户交谈,因此不会重定向到登录页面:

let(:auth_headers) { Devise::JWT::TestHelpers.auth_headers({ 'ACCEPT': 'application/json' }, user) }

# ...


get "/api/v1/something", headers: { 'ACCEPT': 'application/json' }

I believe the issue is that devise returns HTTP 302 Redirect if the request is made from a browser, otherwise it returns 401 Unauthorized.我认为问题在于,如果请求是从浏览器发出的,devise 返回 HTTP 302 Redirect,否则返回 401 Unauthorized。

I believe the solution is to update the requests in your test file to look like this:我相信解决方案是将测试文件中的请求更新为如下所示:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在RSpec中测试Rails视图:当我想测试“索引”时为什么会路由到“显示”? - Testing Rails views in RSpec: why does it route to “show” when I want to test “index”? 预计响应的状态代码为 200,但在 rspec 的请求测试中为 302 - Expected the response to have status code 200 but it was 302 in requests test in rspec Instant Rails 2.0不会更新gems并显示HTTP响应302错误 - Instant Rails 2.0 does not update gems and showing HTTP response 302 error 为什么这个RSpec示例需要“ let!”而不是“ let”? - Why does this RSpec example require “let!” instead of “let”? 无法使用Neo4j db启动Rails服务器,响应coed 401而不是200 - Cannot start rails server with Neo4j db, response coed 401 instead of 200 为什么rails app显示/公共索引列表而不是实际的应用程序? - Why does rails app show /public index listing instead of actual app? 为什么升级到Rails 3.2.1会导致多个Rspec测试失败? - Why does upgrading to Rails 3.2.1 cause multiple Rspec tests to fail? Rails / Rspec:为什么此控制器规格正确路由? - Rails/Rspec: Why does this controller spec route correctly? Rails RSpec:测试“显示”页面 - Rails RSpec: Test the "show" page 在 rspec 中设计重定向而不是 json 响应 - devise redirects instead of json response in rspec
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM