Rails 5应用移动端POST请求被识别为下载问题求助
首先咱们来定位核心问题:你遇到的iOS端下载提示,本质是浏览器没有把这个请求识别成Ajax请求,而是当成了普通的表单提交;再加上返回204无内容状态码,iOS Safari会把空响应当成待下载的文件,这才弹出了下载失败的提示。
下面是具体的修复步骤:
1. 修正button_to的remote: true参数位置
你的button_to代码里把remote: true放在了路由参数哈希里,这会让它变成URL的query参数(就是你看到的?remote=true),而不是让Rails生成UJS需要的data-remote="true"属性。正确写法应该把remote: true移到第二个选项哈希中:
<%= button_to('VOTE FOR FIRST', { controller: 'full_calls', action: 'vote_for_first', id: @full_call }, { class: "btn btn-outline-secondary btn-sm vote-btn", remote: true }) %>
这样Rails会自动给按钮加上data-remote="true",Turbolinks/UJS就会把这个请求处理成Ajax,而不是普通表单提交。
2. 完善控制器的vote_for_first方法
你的控制器方法有两个问题:一是没通过params[:id]找到对应的@full_call,二是没有处理Ajax请求的响应格式,默认返回HTML+204状态码,这会触发iOS的下载行为。修改后的代码如下:
def vote_for_first # 先找到对应的记录 @full_call = FullCall.find(params[:id]) @full_call.first_vote += 1 if @full_call.save # 针对Ajax请求返回JSON/JS,用200状态码避免iOS处理204的问题 respond_to do |format| format.js { render json: { success: true }, status: :ok } # 兼容非Ajax请求的降级处理 format.html { redirect_to @full_call, notice: "Vote recorded!" } end else respond_to do |format| format.js { render json: { error: "Vote failed" }, status: :unprocessable_entity } format.html { redirect_to @full_call, alert: "Failed to record vote" } end end end
3. 调整JS事件绑定逻辑
你原来的JS绑定click事件,但UJS处理Ajax有自己的事件体系,用ajax:success和ajax:error事件会更可靠(确保请求成功后再执行操作,而不是点击就触发):
$(document).on('turbolinks:load', function() { $('.vote-btn') .on('ajax:success', function() { $(this).attr('disabled', true); $('#full-call-alert').append("<h5>Thanks for voting!</h5>").hide().slideDown('slow'); }) .on('ajax:error', function() { $('#full-call-alert').append("<h5>Sorry, your vote didn't go through. Please try again.</h5>").hide().slideDown('slow'); }); });
4. 验证路由参数
你的路由post 'vote_for_first', to: 'full_calls#vote_for_first'是没问题的,修正button_to后,id参数会正确传递到params[:id],控制器能找到对应的记录。
做完这些调整后,iOS Safari应该会正确识别这是Ajax请求,返回200状态码的JSON响应,不会再触发下载提示,用户也能停留在/show页面完成投票。
内容的提问来源于stack exchange,提问作者nobetterjim

