为什么当我尝试访问我的登录视图时会调用show方法?

huangapple go评论69阅读模式
英文:

Why the show method is called when I try to access to my login view?

问题

当我尝试访问我的用户登录视图时,我收到以下错误信息:

为什么当我尝试访问我的登录视图时会调用show方法?

我不明白为什么会调用show方法。

以下是我的路由:

Rails.application.routes.draw do
  resources :comments
  resources :advertisements
  resources :users
  get 'users/login' => 'users#login', as: 'login'
  post 'users/login' => 'users#check', as: 'check'
  delete 'users/logout' => 'users#logout', as: 'logout'
end

以下是我的控制器:

class UsersController < ApplicationController
  before_action :set_user, only: [:show]

  # GET /users
  # GET /users.json
  def index
    @users = User.all
  end

  # GET /users/1
  # GET /users/1.json
  def show
  end

  def login
  end

  def check
    @current_user = User.where(name: params[:name], password: params[:password]).first
    if @current_user
      session[:user_id] = @current_user.id
      flash[:info] = "You're connected!"
      redirect_to root_path
    else
      session[:user_id] = nil
      flash[:info] = "Connection failure"
      redirect_to login_path
    end
  end

  def logout
    session[:user_id] = nil
    flash[:info] = "You're disconnected!"
    redirect_to root_path
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def user_params
      params.require(:user).permit(:name, :password, :role)
    end
end

以下是我的索引视图:

<%= link_to 'Sign up', new_user_path %>
<%= link_to 'Sign in', login_path %>

我漏掉了什么吗?

英文:

I'm learning Rails and I'm trying to do a login feature.

When I try to access to my users/login view, I get this :

为什么当我尝试访问我的登录视图时会调用show方法?

I don't understand why the show method is called.

Here is my routes:

Rails.application.routes.draw do
  resources :comments
  resources :advertisements
  resources :users
  get &#39;users/login&#39; =&gt; &#39;users#login&#39;, as: &#39;login&#39;
  post &#39;users/login&#39; =&gt; &#39;users#check&#39;, as: &#39;check&#39;
  delete &#39;users/logout&#39; =&gt; &#39;users#logout&#39;, as: &#39;logout&#39;
end

Here is my controller:

class UsersController &lt; ApplicationController
  before_action :set_user, only: [:show]

  # GET /users
  # GET /users.json
  def index
    @users = User.all
  end

  # GET /users/1
  # GET /users/1.json
  def show
  end

  def login
  end

  def check
    @current_user = User.where(name: params[:name], password: params[:password]).first
    if @current_user
      session[:user_id] = @current_user.id
      flash[:info] = &quot;You&#39;re connected !&quot;
      redirect_to root_path
    else
      session[:user_id] = nil
      flash[:info] = &quot;Connection failure&quot;
      redirect_to login_path
    end
  end

  def logout
    session[:user_id] = nil
    flash[:info] = &quot;You&#39;re disconnected!&quot;
    redirect_to root_path
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def user_params
      params.require(:user).permit(:name, :password, :role)
    end
end

Here is my index view:

&lt;%= link_to &#39;Sign up&#39;, new_user_path %&gt;
&lt;%= link_to &#39;Sign in&#39;, login_path %&gt;

Did I miss something?

答案1

得分: 1

因为你在路由中将用户声明为资源,所以你的应用程序认为你正在尝试访问 ID 为 'login' 的用户。
当你声明资源时,你会得到以下类型的 URL:

users#index => 'users'

users#show => 'users/1'

这样一来,你的登录 URL 与你的资源发生冲突。通常我会创建一个不同的 URL,比如 '/login',以防止这种情况发生。

英文:

because you are declaring the users as a resource in your routes, your app thinks that your are trying to acces the user with id 'login'.
when you declare the resource, you get urls like:

users#index =&gt; &#39;users&#39;

users#show =&gt; &#39;users/1&#39;

in that way, your login url is colliding with your resources, i usually create a diferent url like '/login' to prevent this to happen

答案2

得分: 1

Fernando是正确的。
请将您的自定义路由移动到resources之前,以免干扰resources生成的路由。

  get 'users/login' => 'users#login', as: 'login'
  post 'users/login' => 'users#check', as: 'check'
  delete 'users/logout' => 'users#logout', as: 'logout'
  resources :users
英文:

Fernando is right.
Please move your custom routes above resources so it won't interfere with resources generated routes

  get &#39;users/login&#39; =&gt; &#39;users#login&#39;, as: &#39;login&#39;
  post &#39;users/login&#39; =&gt; &#39;users#check&#39;, as: &#39;check&#39;
  delete &#39;users/logout&#39; =&gt; &#39;users#logout&#39;, as: &#39;logout&#39;
  resources :users

答案3

得分: 1

更好的方法是使用REST来将会话建模为资源,而不是通过一堆非RESTful(且模糊)的路由来膨胀您的用户控制器。

首先设置 ActiveModel::SecurePassword,以便您不会以明文存储密码,这是一种非常糟糕的做法

class SessionsController < ApplicationController
  # GET /users/login
  def new
  end

  # POST /users/login
  def create
    if @user = User.find_by(params[:email]) && @user.authenticate(params[:password])
      session[:user_id] = @user.id
      redirect_to root_path, success: 'You have been signed in.'
    else
      # 不要告诉用户邮箱实际上是有效的,以避免枚举攻击
      render :new, error: 'Invalid email or password'
    end
  end

  # DELETE /users/logout
  def destroy
    session.delete(:user_id)
    redirect_to root_path, info: 'You have been signed out.'
  end
end

如果您想创建像 /users/login 这样的自定义路由,而不只是 resource :session, only: [:new, :create, :destroy],您可以这样做:

get '/users/login' => 'session#new', as: :login
post '/users/login' => 'session#create'
delete '/users/logout' => 'session#destroy', as: :logout

# 这必须放在下面
resources :users

您还可以在 resources 中的成员之外使用 collection 生成路由:

resources :users do
  collection do
    get :login, to: 'session#new', as: :login
    post :login, to: 'session#create'
    delete :logout, to: 'session#destroy', as: :logout
  end
end

但这将生成命名助手 users_login_path,而不只是 login_path

您不必使用一个笨拙的 /check 路径,因为新建和创建路由通过HTTP动词进行区分。

您可以使用以下方式设置表单:

<h1>Log in</h1>
<%= form_with(url: login_path, method: :post) do |f| %>
  <div class="field">
    <%= f.label :email %>
    <%= f.email_field :email %>
  </div>
  <div class="field">
    <%= f.label :password %>
    <%= f.password_field :password %>
  </div>
  <%= f.submit %>
<% end %>

以及注销按钮:

<%= button_to logout_path, method: :delete %>
英文:

A better way to do this would be to use REST to model sessions as a resource instead of bloating your users controller with a bunch of non-restful (and vague) routes.

Start by setting up ActiveModel::SecurePassword so that you're not storing passwords in plaintext which is a very bad practice.

class SessionsController &lt; ApplicationController
  # GET /users/login
  def new
  end

  # POST /users/login
  def create
    if @user = User.find_by(params[:email]) &amp;&amp; @user.authenticate(params[:password])
      session[:user_id] = @user.id
      redirect_to root_path, success: &#39;You have been signed in.&#39;
    else
      # don&#39;t tell the user if the email was in fact valid 
      # to avoid enumeration attacks
      render :new, error: &#39;Invalid email or password&#39;
    end
  end

  # DELETE /users/logout   
  def destroy
    session.delete(:user_id)
    redirect_to root_path, info: &#39;You have been signed out.&#39;
  end
end

If you want to create vanity routes like /users/login instead of just resource :session, only: [:new, :create, :destroy] you can do it by:

get &#39;/users/login&#39; =&gt; &#39;session#new&#39;, as: :login
post &#39;/users/login&#39; =&gt; &#39;session#create&#39;
delete &#39;/users/logout&#39; =&gt; &#39;session#destroy&#39;, as: :logout

# this has to come after
resources :users

You can also use collection to generate routes on the collection instead of a member in resources:

resources :users do
  collection do
    get :login, to: &#39;session#new&#39;, as: :login
    post :login, to: &#39;session#create&#39;
    delete :logout, to: &#39;session#destroy&#39;, as: :logout
  end
end

But that will generate the named helpers users_login_path instead of just login_path.

You don't have to use a goofy /check path as the new and create routes are differentiated by the HTTP verb.

You can setup the form with:

# app/views/sessions/new.html.erb
&lt;h1&gt;Log in&lt;/h1&gt;
&lt;%= form_with(url: login_path, method: :post) do |f| %&gt;
  &lt;div class=&quot;field&quot;&gt;
    &lt;%= f.label :email %&gt;
    &lt;%= f.email_field :email %&gt;
  &lt;/div&gt;
  &lt;div class=&quot;field&quot;&gt;
    &lt;%= f.label :password %&gt;
    &lt;%= f.password_field :password %&gt;
  &lt;/div&gt;
  &lt;%= f.submit %&gt;
&lt;% end %&gt;

And a button to logout:

&lt;%= button_to logout_path, method: :delete %&gt;

答案4

得分: 0

resources :users 上面有 get 'users/login' => 'users#login', as: 'login',它定义了路由:GET users/:id 并将其链接到你的控制器中的 show 动作。当你访问 users/login 时,Rails 会在你的 routes.rb 文件中寻找第一个匹配的路由,而它将被解释为 show 动作,并且 login 将被解释为 id 参数。

英文:

you have resources :users above get &#39;users/login&#39; =&gt; &#39;users#login&#39;, as: &#39;login&#39; and it defines route: GET users/:id and links it with show action in your controller. When you go to users/login rails will look for first matching route in your routes.rb file and it will be show action and login will be interpreted as id param.

huangapple
  • 本文由 发表于 2020年1月3日 19:49:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/59578119.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定