【问题标题】:Rails: POST 422 (Unprocessable Entity) in Rails? Due to the routes or the controller?Rails:Rails 中的 POST 422(不可处理实体)?由于路线或控制器?
【发布时间】:2015-01-21 18:37:07
【问题描述】:

我试图在我的网站上为发布品牌名称的用户提供“积分”或“信用”。

我在适当的视图上有精美的 twitter 小部件...

<p><a  href="https://twitter.com/share" class="twitter-share-button" data-text="Check Out This Awesome Website Yay" data-via="BrandName" data-hashtags="ProductName">Tweet</a>
<div id="credited"></div>
<script>window.twttr = (function (d, s, id) {
  var t, js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s); js.id = id;
  js.src= "https://platform.twitter.com/widgets.js";
  fjs.parentNode.insertBefore(js, fjs);
  return window.twttr || (t = { _e: [], ready: function (f) { t._e.push(f) } });
}(document, "script", "twitter-wjs"));
</script>    

我把 JS 都写好了,而且很漂亮....

function creditTweet() {
  $.post(
    "/credit_tweet",
    {},
    function(result) {
      var text;
      if (result.status === "noop") {
        text = "Thanks for sharing already!";
      } else if (result.status === "ok") {
        text = "5 Kredit Added";
      }
      $("#credited").html(text);
    }
  );
}

$(function() {
  twttr.ready(function (twttr) {
    window.twttr.events.bind('tweet', creditTweet);
  }); 
});

现在问题出在控制器或路由中(我要发布的地方)。我认为路线很好,因为 POST 几乎可以正常工作,因为这是维基百科上错误的描述 - “422 Unprocessable Entity (WebDAV; RFC 4918) 该请求格式正确,但由于语义错误而无法执行。”

那么,你们看到我在控制器中的 ruby​​ 代码有什么问题吗?

class SocialKreditController < ApplicationController
    TWEET_CREDIT_AMOUNT = 5

  def credit_tweet
    if !signed_in?
      render json: { status: :error }
    elsif   current_user.tweet_credited
        Rails.logger.info "Not crediting #{ current_user.id }"
        render json: { status: :noop }
      else
        Rails.logger.info "Crediting #{ current_user.id }"
        current_user.update_attributes tweet_credited: true
        current_user.add_points TWEET_CREDIT_AMOUNT
        render json: { status: :ok }
      end
  end
end

在我的 routes.rb 中,它非常简单,所以我怀疑这里有什么问题......

  get 'social_kredit/credit_tweet'
  post '/credit_tweet' => 'social_kredit#credit_tweet'

哪里哦,这个错误在哪里?我显然对 HTTP 请求一无所知。

【问题讨论】:

  • 当 ActiveRecord 对象无法更新/保存时,IME rails 通常会返回 422。你的日志说什么?
  • 看起来 .... 于 2014 年 11 月 24 日 00:34:53 -0500 开始 POST "/credit_tweet" for 127.0.0.1 由 SocialKreditController#credit_tweet 处理为 / 无法验证 CSRF 令牌真实性 Completed 422 Unprocessable Entity in 1ms ActionController::InvalidAuthenticityToken - ActionController::InvalidAuthenticityToken:
  • 你能用显示这个错误的行从 Rails 服务器发布日志吗?另外,为什么你有两条路线get 和post 指向同一个方法?

标签: javascript ruby-on-rails ruby ajax httprequest


【解决方案1】:

我搞定了!

我添加了一个...

skip_before_action :verify_authenticity_token

到控制器。

在查看日志并发现无法验证 CSRF 令牌时发现问题。

【讨论】:

  • 您可能还想查看 this question 以获得不需要禁用 csrf 令牌检查的解决方案。
  • 感谢@ihaztehcodez - 此skip_before_action :verify_authenticity_token 仅在用户登录后必需的控制器上使用。是否有任何主要原因我可能想要使用该链接中提供的AJax 解决方案在这个小sn-p上?
  • @piratetone :是的,原因是安全性。
  • The rails security guide 很好地解释了 csrf。通常,您不应在没有充分理由的情况下禁用 csrf 检查(例如来自第三方 API 的回调)。在这种情况下,您完全有能力让 csrf 检查满意,这是最好的做法。
  • 关闭安全是解决方案?
【解决方案2】:

ihaztehcodez(他最后一次活动是在 2016 年,所以它无助于推动他发布答案)提到 skip_before_action :verify_authenticity_token 技术并不那么安全,因为你失去了伪造保护。

他们提到最好/安全/'更好的实践',这里提到了解决方案WARNING: Can't verify CSRF token authenticity rails

例如

$.ajaxSetup({
  headers: {
    'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
  }
});

或

$.ajax({ url: 'YOUR URL HERE',
  type: 'POST',
  beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
  data: 'someData=' + someData,
  success: function(response) {
    $('#someDiv').html(response);
  }
});

或

将其放入 ajax 请求中

headers: {
  'X-Transaction': 'POST Example',
  'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},

【讨论】:

    【解决方案3】:

    我遇到了同样的问题。 加完就搞定了

    skip_before_action :verify_authenticity_token

    在您的 JS 调用或发送数据的控制器顶部。

    class UserController < ApplicationController
        skip_before_action :verify_authenticity_token
        def create
        end
    end
    

    如代码sn-p所示。

    【讨论】:

      【解决方案4】:

      我在开发 Rails 6 仅限 API 的应用程序时遇到了这个挑战。

      我按照这里的答案 - Rails: How to implement protect_from_forgery in Rails API mode 在 Rails API 模式下实现protect_from_forgery,但是当我从邮递员:

      Started POST "/api/v1/programs" for ::1 at 2021-02-23 18:42:49 +0100
      Processing by Api::V1::ProgramsController#create as JSON
        Parameters: {"program"=>{"name"=>"Undergraduate", "code"=>"UD", "affiliate_status"=>false, "motto"=>"Our motto is ...", "description"=>"This is a new program", "school_id"=>1}}
      Can't verify CSRF token authenticity.
        TRANSACTION (0.3ms)  BEGIN
        ↳ app/controllers/api/v1/programs_controller.rb:27:in `create'
        Baserecord::School Load (0.3ms)  SELECT "schools".* FROM "schools" WHERE "schools"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
        ↳ app/controllers/api/v1/programs_controller.rb:27:in `create'
        TRANSACTION (0.4ms)  ROLLBACK
        ↳ app/controllers/api/v1/programs_controller.rb:27:in `create'
      Completed 422 Unprocessable Entity in 30ms (Views: 0.8ms | ActiveRecord: 6.9ms | Allocations: 13172)
      

      这是我解决的问题:

      问题是由我的模型中的验证错误引起的。我为我正在调用的控制器添加到模型中的验证失败。在发出请求以查找错误后,我必须检查 Postman 返回的响应的正文:

      {
          "affiliate_status": [
              "can't be blank"
          ]
      }
      

      一旦我按照这个答案修复了错误 - Rails: Validation fails for ActiveRecord in setting a random Boolean Attribute,之后一切正常。

      就是这样。

      我希望这会有所帮助

      【讨论】:

        【解决方案5】:

        如果您在带有 &lt;%= csrf_meta_tags %&gt; 的 HTML 标头中包含 Rails 元数据,它将生成以下内容。

        <meta name="csrf-param" content="authenticity_token" />
        <meta name="csrf-token" content="ihwlaOLL232ipKmWYaqbSZacpJegQqooJ+Cj9fLF2e02NTQw7P/MfQyRuzruCax2xYWtEHWsb/uqiiZP6NWH+Q==" />
        

        您可以从元数据中提取 CRSF 令牌并将其传递到您的异步请求中。 使用原生 js fetch 方法,您可以将其作为 x-csrf-token 标头传递。

        这是一个用于增强标准 Rails 表单的 React 组件的修剪 onSave 处理程序。

          onSaveHandler = (event) => {
            const data = "Foo Bar";
            const metaCsrf = document.querySelector("meta[name='csrf-token']");
            const csrfToken = metaCsrf.getAttribute('content');
            fetch(`/posts/${this.props.post_id}`, {
              method: "PUT",
              body: JSON.stringify({
                content: data
              }),
              headers: {
                'x-csrf-token': csrfToken,
                'content-type': 'application/json',
                'accept': 'application/json'
              },
            }).then(res => {
              console.log("Request complete! response:", res);
            });
          }
        

        防伪是个好主意。这样我们就可以保持安全并且不会弄乱我们的 Rails 配置。

        使用gem 'rails', '~&gt; 5.0.5' & "react": "^16.8.6",

        【讨论】:

          【解决方案6】:

          在 POST 请求中更新模型时可能会发生 422:

          例如@user.update!(email: nil) 中的 users#update 将生成 422,如果在电子邮件上进行验证,例如 validates :email, presence: true

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-11-18
            • 1970-01-01
            • 1970-01-01
            • 2018-04-17
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多