Rails 7 响应错误 ActionController::UnknownFormat

huangapple go评论84阅读模式
英文:

Rails 7 respond_do Error ActionController::UnknownFormat

问题

我正在尝试在Rails 7中进行Ajax请求,当表单提交时(当按下按钮时),它会呈现JavaScript

在我的股票控制器中,我有:

class StocksController < ApplicationController

    def search
        if params[:stock].present?
            @stock = Stock.new_lookup(params[:stock])
            if @stock
                respond_to do |format|
                    format.js {render partial: 'users/result'}
                end
            else
                flash[:alert] = "请输入有效的符号进行搜索"
                redirect_to my_portfolio_path
            end
        else
            flash[:alert] = "请输入要搜索的符号"
            redirect_to my_portfolio_path
        end
    end
end
我的表单:

<div class="search-area">
  <h3>搜索股票</h3>
  <%= form_tag search_stock_path, method: :get, remote: true do %>
      <div class="form-group row">
          <div class="col-sm-9 noRightPad">
              <%= text_field_tag :stock, params[:stock], placeholder: "股票代码",
              autofocus: true, class: "form-control form-control-lg" %>
          </div>
          <div class="col-sm-3 noLeftPad">
              <%= button_tag type: :submit, class: "btn btn-success" do %>
                  <%= fa_icon "search 2x" %>
              <% end %>
          </div>
      </div>
  <% end %>
</div>

错误信息:
Rails 7 响应错误 ActionController::UnknownFormat

感谢您的时间。

更新
控制器已更新:

class StocksController < ApplicationController
  respond_to :js

  def search
      if params[:stock].present?
          @stock = Stock.new_lookup(params[:stock])
         if @stock
          respond_to do |format|
              format.turbo_stream do
                render turbo_stream: turbo_stream.update(
                  "results",
                  partial: "users/result" # 渲染任何部分并删除js代码。
                )
              end
            end
         else
              flash[:alert] = "请输入有效的符号进行搜索"
              redirect_to my_portfolio_path
         end
      else
          flash[:alert] = "请输入要搜索的符号"
          redirect_to my_portfolio_path
      end

  end
end

表单标签:

<%= form_tag search_stock_path, method: :get, data: {turbo_stream: true}, remote: true do %>
英文:

I am trying to do a Ajax request in rails 7 when the form is submitted (when the button is pressed) it renders JavaScript

In my Stocks Controller I have:

class StocksController &lt; ApplicationController

    def search
        if params[:stock].present?
            @stock = Stock.new_lookup(params[:stock])
           if @stock
                 respond_to do |format|
                     format.js {render partial: &#39;users/result&#39;}
                end
           else
                flash[:alert] = &quot;Please enter a valid symbol to search&quot;
                redirect_to my_portfolio_path
           end
        else
            flash[:alert] = &quot;Please enter a symbol to search&quot;
            redirect_to my_portfolio_path
        end

    end

end

My Form I have:

&lt;div class=&quot;search-area&quot;&gt;
&lt;h3&gt;Search Stocks&lt;/h3&gt;
&lt;%= form_tag search_stock_path, method: :get, remote: true do %&gt;
    &lt;div class=&quot;form-group row&quot;&gt;
        &lt;div class=&quot;col-sm-9 noRightPad&quot;&gt;
            &lt;%= text_field_tag :stock, params[:stock], placeholder: &quot;Stock ticker symbol&quot;, 
            autofocus: true, class: &quot;form-control form-control-lg&quot; %&gt;
        &lt;/div&gt;
        &lt;div class=&quot;col-sm-3 noLeftPad&quot;&gt;
            &lt;%= button_tag type: :submit, class: &quot;btn btn-success&quot; do %&gt;
                &lt;%= fa_icon &quot;search 2x&quot; %&gt;
            &lt;% end %&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;% end %&gt;

</div>

Error Message:
Rails 7 响应错误 ActionController::UnknownFormat

Thank you for your time.

Update
Controller Updated

class StocksController &lt; ApplicationController
respond_to :js

def search
    if params[:stock].present?
        @stock = Stock.new_lookup(params[:stock])
       if @stock
        respond_to do |format|
            format.turbo_stream do
              render turbo_stream: turbo_stream.update(
                &quot;results&quot;,
                partial: &quot;users/result&quot; # render any partial and remove js code.
              )
            end
          end
       else
            flash[:alert] = &quot;Please enter a valid symbol to search&quot;
            redirect_to my_portfolio_path
       end
    else
        flash[:alert] = &quot;Please enter a symbol to search&quot;
        redirect_to my_portfolio_path
    end

end

end

Form Tag

&lt;%= form_tag search_stock_path, method: :get, data: {turbo_stream: true}, remote: true do %&gt;

I have researched the 406 Not acceptable error in console but it says to add respond_to which I have done and still the same issue

答案1

得分: 1

我将为您翻译以下部分:


Headers

Rails将此抽象出来,因此您无需处理它。有两个标头您必须知道:

Content-Type

这是您发送到服务器的请求和您从响应中发送的内容。例如,您可以发送多部分表单数据并获得JSON作为响应(当您需要上传图像到API服务器时)。

Accept

这是您要作为响应获取的内容。这是确定Rails将运行哪个格式块的标头。

Mime类型

这是放入AcceptContent-Type标头的内容。Rails有一个处理它的类:
https://api.rubyonrails.org/classes/Mime/Type.html

Mime::Type.register "text/vnd.hyper-stream.html", :hyper
# 如果您在Accept标头中发送这个,然后运行这个格式块

ActiveSupport.on_load(:action_controller) do
  ActionController::Renderers.add :hyper do |html, options|
    # 如果渲染^ `render hyper: ..`,设置响应类型
    self.content_type = Mime[:hyper] if media_type.nil?
    html
  end
end

# 现在您有了自己的格式
format.hyper { render hyper: ... }

Controller and Form

您知道要使用哪些标头,下面是如何使用它们:

<!-- app/views/stocks/_form.html.erb -->

<!-- 暂时禁用Turbo以防止干扰 -->
<script type="module"> Turbo.session.drive = false </script>

<!-- 制作自己的远程表单 -->
<div id="remote_response"></div>
<%= form_with model: stock, html: {onsubmit: "remote(event)"} do |form| %>
  <%= form.submit %>
<% end %>

*在真实应用程序中使用事件侦听器和事件委托。

<script charset="utf-8">
  function remote(event) {
    event.preventDefault();
    const form = event.target;

    fetch(form.action, {
      // headers: { "Accept": "text/html" },
      // headers: { "Accept": "text/vnd.turbo-stream.html" },
      headers: { "Accept": "application/json" },
      method: form.method,
      body: new FormData(form),
    })
      .then(response => response.text())
      .then(text => {
        document.querySelector("#remote_response").innerHTML = text;
      })
  }
</script>
def create
  puts "# CONTENT TYPE  | #{request.content_type}"  # 您发送的内容
  puts "# ACCEPT        | #{request.accept}"        # 您想要的内容

  # 在`fetch`中更改Accept标头以选择要运行的格式块
  respond_to do |format|
    format.html         { render html:         "Responded with html" }
    format.json         { render json:         "Responded with json" }
    format.js           { render js:           "console.log('railsujs')" }
    format.turbo_stream { render turbo_stream: "Responded with turbo stream" }
  end

  puts "# RESPONSE TYPE | #{response.content_type}" # 您收到的内容
end

{remote: true}

remote: true(如果使用form_with,也可以是local: false)将在表单标签中添加data-remote="true",仅此而已。前端的某些部分必须知道如何处理它。这个部分就是由Turbo在rails 7中取代的RailsUJS

$ bin/importmap pin @rails/ujs

只需选择Turbo或RailsUJS:

// app/javascript/application.js

// import "@hotwired/turbo-rails"
// import "controllers"

import Rails from "@rails/ujs";
Rails.start();
<%= form_with model: stock, local: false do |form| %>
...

现在RailsUJS将执行我们在remote()函数中所做的操作,并将Accept设置为text/javascript,以便运行format.js块。它还会处理响应并执行代码。


Turbo

流和帧以及广播需要一点时间来理解。首先从turbo_stream开始,我发现这样更容易理解。

无需设置,所有表单都以TURBO_STREAM格式“远程”提交,即"Accept": "text/vnd.turbo-stream.html, text/html, application/xhtml+xml"。这意味着您可以响应format.htmlformat.turbo_stream

respond_to do |format|
  format.turbo_stream do
    render turbo_stream: turbo_stream.update(
      "id_of_the_element_to_update",
      partial: "users/result" # 渲染任何部分并删除js代码。
    )
  end
end

更新

GET

对于method: :get和链接,将data-turbo-stream="true"添加到表单中。

form_tag "/", method: :get, data: {turbo_stream: true} do

https://turbo.hotwired.dev/handbook/streams#streaming-from-http-responses

引导

您不能导入"bootstrap",除非您有一个pin:

import * as bootstrap from "bootstrap"
//                          ^^^^^^^^^
// 浏览器不知道如何获取它

添加一个pin(并从cdn获取样式):

bin/importmap pin bootstrap

如果importmaps给您带来了太多麻烦,可以使用cssbundling-rails。Rails已经内置了这个功能:

rails new my_app -c bootstrap

但是有很多其他关于如何在Rails 7中安装bootstrap的答案。

调试

在处理JavaScript时,您必须查看浏览器控制台。

英文:

I'll try to demystify the magic here, I've seen this question come up a lot.

Setup:

rails new rails_formats -c tailwind
cd rails_formats
bin/rails g scaffold stock name
bin/rails db:migrate
open http://localhost:3000/stocks/new
bin/dev

Headers

Rails abstracts this out of the away so you don't have to deal with this. There are two headers that you just have to know:

Content-Type

This is what you send to the server with your request, and what you send back with the response. For example, you can send multipart form data and get json as a response (when you need to upload images to your api server).

Accept

This is what you want to get as a response. This is the one that determines what format block rails will run.

Mime types

It's what goes into Accept and Content-Type headers. Rails has a class to handle it:
https://api.rubyonrails.org/classes/Mime/Type.html

Mime::Type.register &quot;text/vnd.hyper-stream.html&quot;, :hyper
# if you send this in Accept ^ header, then run this ^ format block

ActiveSupport.on_load(:action_controller) do
  ActionController::Renderers.add :hyper do |html, options|
    # set response type if rendering ^ `render hyper: ..`
    self.content_type = Mime[:hyper] if media_type.nil?
    html
  end
end

# now you have your own format
format.hyper { render hyper: ... }

Controller and Form

You know what headers to use, here is how to use them:

&lt;!-- app/views/stocks/_form.html.erb --&gt;

&lt;!-- disable Turbo for now so it doesn&#39;t interfere --&gt;
&lt;script type=&quot;module&quot;&gt; Turbo.session.drive = false &lt;/script&gt;

&lt;!-- make your own remote form --&gt;
&lt;div id=&quot;remote_response&quot;&gt;&lt;/div&gt;
&lt;%= form_with model: stock, html: {onsubmit: &quot;remote(event)&quot;} do |form| %&gt;
  &lt;%= form.submit %&gt;
&lt;% end %&gt;

*Use event listeners and event delegation in real apps.

&lt;script charset=&quot;utf-8&quot;&gt;
  function remote(event) {
    event.preventDefault();
    const form = event.target;

    fetch(form.action, {
      // headers: { &quot;Accept&quot;: &quot;text/html&quot; },
      // headers: { &quot;Accept&quot;: &quot;text/vnd.turbo-stream.html&quot; },
      headers: { &quot;Accept&quot;: &quot;application/json&quot; },
      method: form.method,
      body: new FormData(form),
    })
      .then(response =&gt; response.text())
      .then(text =&gt; {
        document.querySelector(&quot;#remote_response&quot;).innerHTML = text;
      })
  }
&lt;/script&gt;
def create
  puts &quot;# CONTENT TYPE  | #{request.content_type}&quot;  # what you sent
  puts &quot;# ACCEPT        | #{request.accept}&quot;        # what you want

  # Change Accept header in `fetch` to choose which format block to run
  respond_to do |format|
    format.html         { render html:         &quot;Responded with html&quot; }
    format.json         { render json:         &quot;Responded with json&quot; }
    format.js           { render js:           &quot;console.log(&#39;railsujs&#39;)&quot; }
    format.turbo_stream { render turbo_stream: &quot;Responded with turbo stream&quot; }
  end

  puts &quot;# RESPONSE TYPE | #{response.content_type}&quot; # what you get
end

{remote: true}

remote: true (or local: false if using form_with) will add data-remote=&quot;true&quot; to the form tag and that's it. Something on the frontend has to know what to do with it. That something is RailsUJS which was replaced by Turbo in rails 7.

$ bin/importmap pin @rails/ujs

Just pick Turbo or RailsUJS:

// app/javascript/application.js

// import &quot;@hotwired/turbo-rails&quot;
// import &quot;controllers&quot;

import Rails from &quot;@rails/ujs&quot;;
Rails.start();
&lt;%= form_with model: stock, local: false do |form| %&gt;
...

Now RailsUJS will do what we did with remote() function and set Accept to text/javascript so that format.js block would run. It will also handle the response and execute the code.


Turbo

Streams and frames and broadcasts take a little bit to get your head around it. Just start with turbo_stream I found it much easier to understand at first.

No set up needed, all forms submit "remotely" as TURBO_STREAM format aka &quot;Accept&quot;: &quot;text/vnd.turbo-stream.html, text/html, application/xhtml+xml&quot;. This means you can respond with format.html or format.turbo_stream.

respond_to do |format|
  format.turbo_stream do
    render turbo_stream: turbo_stream.update(
      &quot;id_of_the_element_to_update&quot;,
      partial: &quot;users/result&quot; # render any partial and remove js code.
    )
  end
end

Update

GET streams

Add data-turbo-stream=&quot;true&quot; to forms with method: :get and links.

form_tag &quot;/&quot;, method: :get, data: {turbo_stream: true} do

https://turbo.hotwired.dev/handbook/streams#streaming-from-http-responses

Bootstrap

You can't import "bootstrap" unless you have a pin for it:

import * as bootstrap from &quot;bootstrap&quot;
//                          ^^^^^^^^^
// browser doesn&#39;t know how to get that 

Add a pin (and get styles from cdn):

bin/importmap pin bootstrap

If importmaps add too much hassle use cssbundling-rails. Rails has this built in:

rails new my_app -c bootstrap

But there are lots of other answers on how to install bootstrap in rails 7.

Debug

Working with javascript, you have to look at your browser console.

huangapple
  • 本文由 发表于 2023年7月24日 15:49:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/76752369.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定