[英]Rails - same route for more than one of the same http methods
Is there any way that it's possible for me to have the same URL for multiple actions (in different controllers) where the HTTP method is the same? 对于HTTP方法相同的多个动作(在不同的控制器中),我是否有可能使我拥有相同的URL?
eg 例如
get 'users/:username/profile' => 'users#profile', as: :user_profile
put 'users/:username/profile' => 'users#avatar_upload', as: :edit_user_avatar
post 'users/:username/profile' => 'users#update', as: :user_edit_profile
post 'users/:username/profile' => 'userspayment#create', as: :add_user_payment_details
post 'users/:username/profile' => 'userspayment#update', as: :user_edit_payment_details
Currently I have: 目前我有:
get 'users/:username/profile' => 'users#profile', as: :user_profile
put 'users/:username/profile' => 'users#avatar_upload', as: :edit_user_avatar
post 'users/:username/profile' => 'users#update', as: :user_edit_profile
post 'users/:username/profile/payment-details/add' => 'userspayment#create', as: :add_user_payment_details
post 'users/:username/profile/payment-details/edit' => 'userspayment#update', as: :user_edit_payment_details
But when I run the add
or update
method in userspayment_controller.rb
I'm rendering the users/profile
view afterwards and the URL changes. 但是,当我在
userspayment_controller.rb
运行add
或update
方法时,我随后将呈现users/profile
视图,并且URL会更改。 I don't want to redirect because the add and update forms are in tabs and a redirect will make the first tab active. 我不想重定向,因为添加和更新表单位于选项卡中,并且重定向将使第一个选项卡处于活动状态。 This is my
UserpaymentsController
: 这是我的
UserpaymentsController
:
class UserspaymentController < ApplicationController
before_action :authenticate_user!
before_action :set_user
def create
respond_to do |format|
@user_payment_details = UsersPaymentDetails.new(users_payment_details_params)
if @user_payment_details.save
record_activity('Updated payment information.')
format.html { render :template => "users/profile", :locals => { :user => @user }, notice: 'Payment details was successfully updated.' }
format.json { render :show, status: :ok, location: @user }
else
format.html { render(:file => Rails.root.join('public', '422'), :formats => [:html], :status => 422, :layout => false) }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
@user_payment_details = UsersPaymentDetails.find_by(user_id: @user.id)
if @user_payment_details.update(users_payment_details_params)
record_activity('Updated payment information.')
format.html { render :template => "users/profile", :locals => { :user => @user }, notice: 'Payment details was successfully updated.' }
format.json { render :show, status: :ok, location: @user }
else
format.html { render(:file => Rails.root.join('public', '422'), :formats => [:html], :status => 422, :layout => false) }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = User.find_by(username: params[:username])
end
# Never trust parameters from the scary internet, only allow the white list through.
def users_payment_details_params
params.require(:users_payment_details).permit(:bank, :branch, :code, :account, :holder, :user_id)
end
end
As the other comment stated, your approach isn't going to work. 正如另一条评论所述,您的方法行不通。 However...
然而...
In your routes.rb
, you could do: 在您的
routes.rb
,您可以执行以下操作:
resources :users, param: :user_name
scope :users do
patch ':user_name/*other', controller: :users, action: :update
get ':user_name/*other', controller: :users, action: :show
post ':user_name/*other', controller: :users, action: :create
end
Which will give you: 这会给你:
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:user_name/edit(.:format) users#edit
user GET /users/:user_name(.:format) users#show
PATCH /users/:user_name(.:format) users#update
PUT /users/:user_name(.:format) users#update
DELETE /users/:user_name(.:format) users#destroy
PATCH /users/:user_name/*other(.:format) users#update
GET /users/:user_name/*other(.:format) users#show
POST /users/:user_name/*other(.:format) users#create
Now, when you have a url
like: 现在,当您使用类似以下的
url
:
/users/foo-bar/profile
it will route to the appropriate update
, show
, or create
action (depending on the HTTP verb) with a params[:other]
equal to profile
. 它将使用等于
profile
的params[:other]
路由到适当的update
, show
或create
动作(取决于HTTP动词)。
In your ApplicationController
, you could do something like: 在您的
ApplicationController
,您可以执行以下操作:
class ApplicationController < ActionController::Base
private
def call_action_service
action_service.call params.except(:controller, :action, :other)
end
def action_service
"#{params[:controller].camelize}::#{params[:other].gsub('-','_').camelize}::#{params[:action].camelize}Service".constantize
end
end
So, if you did a get
on /users/foo-bar/profile
, then action_service
would resolve to: 因此,如果您
get
了/users/foo-bar/profile
,那么action_service
将解析为:
Users::Profile::ShowService
And you could make your UsersController
something like: 您可以使您的
UsersController
类似:
class UsersController < ApplicationController
def show
unless params[:other]
# do regular show
else
call_action_service
end
end
def update
unless params[:other]
# do regular update
else
call_action_service
end
end
def create
unless params[:other]
# do regular create
else
call_action_service
end
end
private
end
You'll note that you have only the RESTful actions, now ( show
, update
, create
, etc.) and have done away with non-RESTful actions (like profile
, and avatar_upload
). 您会注意到,您现在只有RESTful动作(
show
, update
, create
等),并且取消了非RESTful动作(如profile
和avatar_upload
)。
So, in the end, the following HTTP verbs
and url
s will call the services: 因此,最后,以下
HTTP verbs
和url
将调用服务:
GET /users/:user_name/profile == calls ==> Users::Profile::ShowService
PATCH /users/:user_name/profile == calls ==> Users::Profile::UpdateService
PATCH /users/:user_name/profile/avatar == calls ==> Users::Profile::Avatar::UpdateService
PATCH /users/:user_name/profile/payment-details == calls ==> Users::Profile::PaymentDetails::UpdateService
POST /users/:user_name/profile/payment-details == calls ==> Users::Profile::PaymentDetails::CreateService
If we continue with the example of making a get
call to /users/foo-bar/profile
, then you'll need a Users::Profile::ShowService
: 如果继续以对
/users/foo-bar/profile
进行get
调用为例,那么您将需要一个Users::Profile::ShowService
:
#app/services/users/profile/show_service.rb
module Users
module Profile
class ShowService < ServiceBase
def call
# do useful stuff
end
end
end
end
You'll also need ServiceBase
: 您还需要
ServiceBase
:
#app/services/service_base.rb
class ServiceBase
attr_accessor *%w(
args
).freeze
class << self
def call(args={})
new(args).call
end
end
def initialize(args)
@args = args
end
def call
# this is the fall back `call` method for all services
end
end
To make generating your paths easier, you might do something like: 为了使生成路径更容易,您可以执行以下操作:
#app/helpers/application_helper.rb
module ApplicationHelper
def other_user_path(other)
user_path(@user.name).send(:<<, "/#{other}")
end
end
So that you could do something like: 这样您可以执行以下操作:
other_user_path('profile/payment-details')
and get 并得到
/users/foo-bar/profile/payment-details
Naturally, you'll want to add back in your respond_to do |format|
自然地,您将需要重新添加回覆以
respond_to do |format|
stuff. 东西。 But, I'll leave that to you to fiddle with.
但是,我会留给您摆弄。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.