64

アプリにバックボーン モデルがありますが、これは典型的なフラット オブジェクトではありません。これは大きなネストされたオブジェクトであり、ネストされた部分を MySQL データベースの TEXT 列に格納します。

Rails API で JSON エンコーディング/デコーディングを処理して、その一部が文字列化された JSON テキストとして格納されている場合でも、この 1 つの大きなネストされた JSON オブジェクトを POST/GET できるように見えるようにしたかったのです。

nilしかし、Rails が魔法のように空の配列を値に変換するという問題に遭遇しました。たとえば、これを投稿した場合:

{
  name: "foo",
  surname: "bar",
  nested_json: {
    complicated: []
  }
}

私のRailsコントローラーはこれを見ます:

{
  :name => "foo",
  :surname => "bar",
  :nested_json => {
    :complicated => nil
  }
}

そして、私のJSONデータが変更されました..

以前にこの問題に遭遇した人はいますか? Rails が私の POST データを変更するのはなぜですか?

アップデート

彼らがそれを行う場所は次のとおりです。

https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/http/request.rb#L288

そして、これが〜なぜ彼らがそれをするのかです:

https://github.com/rails/rails/pull/8862

さて、問題は、ネストされた JSON API の状況でこれをどのように処理するのが最善かということです。

4

6 に答える 6

45

よく調べた結果、Rails 4.1 以降では、deep_munge の「機能」を完全にスキップできることがわかりました。

config.action_dispatch.perform_deep_munge = false

ドキュメントは見つかりませんでしたが、このオプションの紹介はこちらで確認できます

これを行うと、セキュリティ リスクが生じる可能性があります。詳細については、https: //groups.google.com/forum/# !topic/rubyonrails-security/t1WFuuQyavI をご覧ください。

于 2014-08-21T14:20:28.330 に答える
11

これは、最近導入された既知の問題のようです: https://github.com/rails/rails/issues/8832

空の配列がどこにあるかがわかっている場合は、常にparams[:...][:...] ||= []before フィルターを使用できます。

または、BackBone モデルを JSON メソッドに変更し、nested_json 値JSON.stringify()がポストされる前に明示的に文字列化し、before_filter を使用して手動で解析することもできJSON.parseます。

醜いですが、うまくいきます。

于 2013-02-01T14:18:12.893 に答える
9

次のように、自分でパラメーターを再解析できます。

class ApiController
  before_filter :fix_json_params    # Rails 4 or earlier
  # before_action :fix_json_params  # Rails 5

  [...]

  protected

  def fix_json_params
    if request.content_type == "application/json"
      @reparsed_params = JSON.parse(request.body.string).with_indifferent_access
    end
  end

  private

  def params
    @reparsed_params || super
  end
end

これは、JSON コンテンツ タイプのリクエストを探し、リクエスト ボディを再解析し、paramsメソッドをインターセプトして、再解析されたパラメーターが存在する場合はそれを返すことによって機能します。

于 2013-02-26T21:30:25.343 に答える
3

同様の問題に遭遇しました。

空の文字列を配列の一部として送信することで修正しました。

したがって、理想的には、パラメータが好きなはずです

{
  name: "foo",
  surname: "bar",
  nested_json: {
    complicated: [""]
  }
}

したがって、空の配列を送信する代わりに、リクエストで常に ("") を渡して、ディープマンジングプロセスをバイパスします。

于 2015-06-15T11:58:16.597 に答える
2

生のリクエスト本文を再解析する必要のない合理的な解決策を次に示します (私は信じています)。クライアントがフォーム データを POST している場合、これは機能しない可能性がありますが、私の場合は JSON を POST しています。

application_controller.rb:

  # replace nil child params with empty list so updates occur correctly
  def fix_empty_child_params resource, attrs
    attrs.each do |attr|
      params[resource][attr] = [] if params[resource].include? attr and params[resource][attr].nil?
    end
  end

次に、コントローラーで....

before_action :fix_empty_child_params, only: [:update]

def fix_empty_child_params
  super :user, [:child_ids, :foobar_ids]
end

私はこれに遭遇し、私の状況では、POST されたリソースにいずれchild_ids: []かが含まれているか、child_ids: nilその更新が「すべての子を削除する」ことを意味するようにしたい場合です。クライアントがリストを更新しない場合child_idsは、POST 本文で送信しないでください。その場合は送信されparams[:resource].include? attrfalseリクエスト パラメータは変更されません。

于 2016-07-11T21:05:38.257 に答える
1

同様の問題に遭遇し、上記のように、空の文字列を含む配列を渡すと、Rails によって正しく処理されることがわかりました。フォームの送信中にこれが発生した場合は、配列 param に一致する空の非表示フィールドを含めることをお勧めします。

<input type="hidden" name="model[attribute_ids][]"/>

実際のパラメーターが空の場合、コントローラーは常に空の文字列の配列を認識し、送信をステートレスに保ちます。

于 2016-03-09T12:51:12.790 に答える