【问题标题】:No route matches [GET] when trying to [PATCH] with link_to尝试使用 link_to [PATCH] 时没有路由匹配 [GET]
【发布时间】:2022-11-15 10:56:06
【问题描述】:

我想向未完成结帐的客户发送一封带有魔术链接的电子邮件,该链接将使他们在控制器中点击update 操作之前登录。

我在电子邮件正文中发送以下链接:

<%= link_to(
           "Continue to checkout",
           "#{checkout_url(host: @account.complete_url, id: @user.current_subscription_cart)}?msgver=#{@user.create_message_verifier}",
           method: :patch,
           subscription_cart: { item_id: @item_id },
           ) %>

我的checkouts_controller 有一个update 动作:

def update
  # update cart with item_id param and continue
end

我的routes 看起来像这样:

resources :checkouts, only: [:create, :update]

它给出了以下update 路线:

checkout_path   PATCH   /checkouts/:id(.:format)    checkouts#update

电子邮件正文中的 link_to 生成一个带有 data-method="patch" 属性的链接

<a data-method="patch" href="https://demo.test.io/checkouts/67?msgver=TOKEN">Continue to checkout</a>

=> https://demo.test.io/checkouts/67?msgver=TOKEN

但是当我点击它时,出现以下错误:

No route matches [GET] "/checkouts/67"

当我指定 method: :patch 时,为什么它会尝试 GET 请求?

【问题讨论】:

  • method: :patch 需要 rails-ujs 才能正常工作。该库在用户接收电子邮件的电子邮件客户端中不可用。您应该假设发送给用户的电子邮件中的所有链接都是 GET 请求

标签: ruby-on-rails ruby routes


【解决方案1】:

正如@AbM 所指出的,您需要发送一个指向响应 GET 请求的路由的链接。电子邮件客户端不太可能让您运行 JS 或在电子邮件正文中包含表单 - 因此您不应该假设您可以发送除 GET 之外的任何内容。

如果你想要一个如何做到这一点的例子,你不必再看 Devise::Confirmable 模块,它解决了几乎完全相同的问题:

Prefix Verb           Method      URI Pattern               Description
new_user_confirmation GET         /users/confirmation/new   Form for resending the confirmation email
user_confirmation     GET         /users/confirmation       The link thats mailed out with a token added - actually confirms the user
                      POST        /users/confirmation       Resends the confirmation email

这种设计的美妙之处在于,即使不存在单独的模型,用户确认也被建模为 RESTful 资源。

在您的情况下,实现可能类似于:

resources :checkouts do
  resource :confirmation, only: [:new, :create, :show]
end
# Handles email confirmations of checkouts
class ConfirmationsController < ApplicationController
  before_action :set_checkout

  # GET /checkouts/1/confirmation/new
  # Form for resending the confirmation email
  def new
  end

  # POST /checkouts/1/confirmation
  # Resends the confirmation email - because shit happens.
  def create
    @todo generate new token and resend the confirmation email
  end

  # GET /checkouts/:checkout_id/confirmation
  # Confirms the checkout by verifying that a valid token is passed
  def show
    if @checkout.valid_token?(params[:token])
      @checkout.confirm!
      redirect_to '/whatever_the_next_step_is'
    else
      flash.now('Invalid token')
      render :new, status: :unauthorized
    end
  end

  private

  def set_checkout
    @checkout = Checkout.find(params[:checkout_id])
  end
end

在这里,我们对 GET 请求应始终是幂等的规则稍作改动,因为单击链接实际上会更新结帐。这是一个公平的权衡,因为它需要用户少点击一次。

如果你想保持幂等性,你可以发送一个链接到 edit 操作,其中包含一个表单到 update 确认。 Devise::Invitable 就是这样做的。

【讨论】:

    猜你喜欢
    • 2015-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多