如何在Rails 7中无需刷新页面渲染控制器自定义动作结果
我在Task控制器中编写了自定义动作check_url,包含if-else逻辑,会调用Google Safe Browsing API返回检测结果。现在需要将结果渲染到index.html.erb页面,且无需刷新页面。目前用render plain: "" and return会在新页面展示结果,试过Ajax没成功,觉得Hotwire/Turbo教程太复杂,想要简单解决方案。
相关代码
tasks_controller.rb中的check_url动作
def check_url @id = params[:id] @result = params[:result] if params[:link].present? api_key = ENV[""] # Set up the request parameters url = params[:link] formatted_url = canonicalize_url(url) threat_types = ["MALWARE", "THREAT_TYPE_UNSPECIFIED", "SOCIAL_ENGINEERING", "UNWANTED_SOFTWARE", "POTENTIALLY_HARMFUL_APPLICATION"] platform_types = ["ANY_PLATFORM"] body = { client: { clientId: api_key, clientVersion: "1.0.0", }, threatInfo: { threatTypes: threat_types, platformTypes: platform_types, threatEntryTypes: %w[URL THREAT_ENTRY_TYPE_UNSPECIFIED EXECUTABLE], threatEntries: [ { url: formatted_url }, ], }, } # Set up the request headers headers = { "Content-Type" => "application/json", } # Make the request to the Safe Browsing API response = HTTP.post( "https://safebrowsing.googleapis.com/v4/threatMatches:find?key=", json: body, headers: headers, ) # Check the response if response.code == 200 data = JSON.parse(response.body) puts JSON.pretty_generate(data) @result = response.body if data["matches"].try(:empty?) render plain: "The URL is safe" and return else render plain: "The URL is not safe" and return end else render plain: "An error occurred" end else puts "link variable is empty" end puts "Response code: #{response.code}" puts "Response body: #{response.body}" puts "Request parameters: #{body}" puts "Request headers: #{headers}" end private def task_params params.require(:task).permit(:link, :result) end
_form.html.erb中的提交表单
<%= form_tag("/check_url", method: "post") do %> <%= label_tag(:link, "Enter a link:") %> <%= text_field_tag(:link) %> <%= submit_tag("Submit") %> <% end %>
index.html.erb代码
<% if notice.present? %> <p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"> <%= notice %> </p> <% end %> <h1 class="font-bold text-2xl">Task Manager</h1> <div class="mt-4"> <%= render "form", task: @task %> <%= render 'tasks/check_url' %> </div>
用Rails内置的UJS(Unobtrusive JavaScript)就能实现无刷新提交,无需复杂原生Ajax或Hotwire:
步骤1:修改表单,启用AJAX提交
在_form.html.erb的form_tag中添加remote: true,让表单通过AJAX提交而非刷新页面:
<%= form_tag("/check_url", method: "post", remote: true) do %> <%= label_tag(:link, "Enter a link:") %> <%= text_field_tag(:link) %> <%= submit_tag("Submit") %> <% end %>
步骤2:修改控制器,响应JS请求
将check_url动作中的render plain:改为设置@message变量,然后渲染JS模板:
def check_url @id = params[:id] @result = params[:result] @message = "" if params[:link].present? api_key = ENV["GOOGLE_SAFE_BROWSING_KEY"] # 替换为你的API密钥环境变量名 url = params[:link] formatted_url = canonicalize_url(url) threat_types = ["MALWARE", "THREAT_TYPE_UNSPECIFIED", "SOCIAL_ENGINEERING", "UNWANTED_SOFTWARE", "POTENTIALLY_HARMFUL_APPLICATION"] platform_types = ["ANY_PLATFORM"] body = { client: { clientId: api_key, clientVersion: "1.0.0", }, threatInfo: { threatTypes: threat_types, platformTypes: platform_types, threatEntryTypes: %w[URL], # 仅保留URL类型即可,简化请求 threatEntries: [ { url: formatted_url }, ], }, } headers = { "Content-Type" => "application/json", } response = HTTP.post( "https://safebrowsing.googleapis.com/v4/threatMatches:find?key=#{api_key}", # 拼接API密钥 json: body, headers: headers, ) if response.code == 200 data = JSON.parse(response.body) puts JSON.pretty_generate(data) @result = response.body if data["matches"].try(:empty?) @message = "The URL is safe" else @message = "The URL is not safe" end else @message = "An error occurred" end else @message = "Please enter a link" end # 渲染JS模板,替代plain文本输出 respond_to do |format| format.js end end
注意:补全
ENV[""]中的API密钥环境变量名,同时把请求URL里的key=改为key=#{api_key},否则API调用会失败。
步骤3:创建JS模板文件
在app/views/tasks/目录下新建check_url.js.erb文件,用来更新页面结果区域:
// 将检测结果插入到指定容器中 document.getElementById('url-check-result').innerHTML = "<p><%= j @message %></p>";
j是escape_javascript的简写,用于转义字符串特殊字符,避免JS执行报错。
步骤4:在index页面添加结果容器
修改index.html.erb,添加一个用于显示结果的div,并设置id为url-check-result:
<% if notice.present? %> <p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"> <%= notice %> </p> <% end %> <h1 class="font-bold text-2xl">Task Manager</h1> <div class="mt-4"> <%= render "form", task: @task %> <!-- 新增结果显示容器 --> <div id="url-check-result" class="mt-4"></div> </div>
删除原有的
<%= render 'tasks/check_url' %>,结果将通过JS直接更新到上述容器中。
验证功能
启动Rails服务器,提交链接后,页面不会刷新,检测结果会直接显示在表单下方的url-check-result容器内。
内容的提问来源于stack exchange,提问作者Nasser FN

