Rails:Rails 中的 POST 422(不可处理实体)?由于路线或控制器?

Posted

技术标签:

【中文标题】Rails:Rails 中的 POST 422(不可处理实体)?由于路线或控制器?【英文标题】:Rails: POST 422 (Unprocessable Entity) in Rails? Due to the routes or the controller? 【发布时间】: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 服务器发布日志吗?另外,为什么你有两条路线getpost 指向同一个方法? 【参考方案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

【讨论】:

以上是关于Rails:Rails 中的 POST 422(不可处理实体)?由于路线或控制器?的主要内容,如果未能解决你的问题,请参考以下文章

从 iOS 客户端返回状态 422 到 Rails 应用程序的 PATCH

Rails/Devise 登录在 Safari 上不起作用(422/CSRF 错误)

AWS Elastic Beanstalk 上的 Rails 应用程序返回 422 并显示“无法验证 csrf 令牌”

Rails API 422 无法处理的实体:没有可用的验证密钥,heroku

(Rails)从不同视图中的控制器访问方法

不返回数组 - Rails 模型函数