如何在Rails控制器destroy动作中调用外部API删除无本地ID的资源?
嘿,我来帮你搞定这个问题!你现在的核心需求是不用本地资源ID,直接通过Rails的destroy动作触发外部API的取消订阅操作,对吧?咱们一步步来调整:
你当前的困境是:要在Rails控制器的destroy动作里调用外部API删除订阅资源,但这个资源并未存储在本地数据库,没法传递常规的ID参数,现有的路由、视图和控制器逻辑还有些细节需要调整。
1. 调整路由配置
你现在用的resources :subscribe, except: :edit会生成带ID的RESTful路由,但我们完全不需要ID,所以改成单资源路由(singular resource)就完美解决了——它不会要求ID参数:
# 如果你想用subscription作为资源名 resource :subscription, only: [:destroy] # 或者保留你原来的subscribe命名 resource :subscribe, only: [:destroy]
单资源路由生成的路径是/subscribe(而非/subscribe/:id),对应的控制器动作就是SubscribeController#destroy,刚好匹配你的需求。
2. 优化Concern与控制器逻辑
先修正代码里的小问题,比如变量拼写、状态判断逻辑,还要确保能正确拿到存在数据库里的subscription_id和client_id:
修正后的Concern代码
module Unsubscribable extend ActiveSupport::Concern def unsubscribe_user @external_api = ExternalApi.new(@key1, @key2) @subscription = @external_api.create(:subscription) # 从当前用户的历史记录里取出存储的subscription_id和client_id # 这里要根据你的实际模型结构调整查询逻辑 historical_record = current_user.historical_subscriptions.last @unsubscribed = @subscription.delete(historical_record.subscription_id, historical_record.client_id) end end
注意:@key1和@key2要确保能正确获取(比如从环境变量或当前用户的配置中读取),历史记录的查询逻辑也要贴合你的实际数据结构。
修正后的控制器代码
class SubscribeController < ApplicationController include Unsubscribable def destroy unsubscribe_user # 这里要根据外部API的返回值调整判断逻辑——通常API成功会返回true/非空响应,失败则返回false/nil if @unsubscribed flash[:notice] = "Successfully unsubscribed..." else flash[:error] = "An error has occurred..." end redirect_to root_path end end
你原来的if @unsubscribed.nil?逻辑大概率是反的,调整后更符合常规API的返回逻辑,具体可以根据ExternalApi#delete的实际返回值来微调。
3. 修正视图代码
先修正路径拼写错误(suscription_path要改成subscribe_path或subscription_path,对应你的路由命名),然后简化button_to的写法:
<p>Unsubscribe <%= button_to "Cancel my Subscription", subscribe_path, method: :delete %></p>
用method: :delete是Rails的标准写法,不需要手动指定{:action => :delete},这样生成的表单会正确发送DELETE请求到/subscribe路径。
- API错误捕获:给外部API调用加上异常捕获,避免因为网络问题、API超时或错误导致整个请求崩溃:
def unsubscribe_user @external_api = ExternalApi.new(@key1, @key2) @subscription = @external_api.create(:subscription) historical_record = current_user.historical_subscriptions.last begin @unsubscribed = @subscription.delete(historical_record.subscription_id, historical_record.client_id) rescue StandardError => e Rails.logger.error "Unsubscribe failed: #{e.message}" @unsubscribed = false end end - 验证历史记录存在:如果用户没有历史记录,要提前处理,避免调用API时出现空指针错误。
内容的提问来源于stack exchange,提问作者Carlos Gómez

