英文:
Why the show method is called when I try to access to my login view?
问题
当我尝试访问我的用户登录视图时,我收到以下错误信息:
我不明白为什么会调用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 :
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 'users/login' => 'users#login', as: 'login'
post 'users/login' => 'users#check', as: 'check'
delete 'users/logout' => 'users#logout', as: 'logout'
end
Here is my controller:
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
Here is my index view:
<%= link_to 'Sign up', new_user_path %>
<%= link_to 'Sign in', login_path %>
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 => 'users'
users#show => 'users/1'
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 'users/login' => 'users#login', as: 'login'
post 'users/login' => 'users#check', as: 'check'
delete 'users/logout' => 'users#logout', as: 'logout'
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 < 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
# don't tell the user if the email was in fact valid
# to avoid enumeration attacks
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
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 '/users/login' => 'session#new', as: :login
post '/users/login' => 'session#create'
delete '/users/logout' => 'session#destroy', 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: 'session#new', as: :login
post :login, to: 'session#create'
delete :logout, to: 'session#destroy', 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
<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 %>
And a button to logout:
<%= button_to logout_path, method: :delete %>
答案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 'users/login' => 'users#login', as: 'login'
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论