POST 422 (необработанная организация) в Rails? Из-за маршрутов или контроллера?

Я пытаюсь дать пользователям на своем сайте "баллы" или "кредиты" для твитов о бренд-имени.

У меня есть привлекательный виджет твиттера на соответствующем представлении...

<p><a  href="#" onclick="location.href='https://twitter.com/share'; return false;" 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 почти работает, потому что это описание ошибки в wikipedia - "422 Unprocessable Entity (WebDAV; RFC 4918) Запрос был хорошо сформирован, но из-за семантических ошибок его не удалось выполнить ".

Итак, вы, ребята, видите что-то не так с моим рубиновым кодом в контроллере?

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

И на моих маршрутах .rb это довольно прямолинейно, поэтому я сомневаюсь, что здесь что-то не так...

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

Где о, где эта ошибка? Я явно не знаю smack о HTTP-запросах.

Ответ 1

У меня все получилось!

Я добавил...

skip_before_action :verify_authenticity_token

к контроллеру.

Проблема была обнаружена при проверке журналов и наблюдении, что токен CSRF не может быть проверен.

Ответ 2

С той же проблемой я столкнулся. Сортируется после добавления

skip_before_action: verify_authenticity_token

в верхней части вашего контроллера, где ваш JS вызывает или отправляет данные.

class UserController < ApplicationController
    skip_before_action :verify_authenticity_token
    def create
    end
end

как показано в фрагменте кода.

Ответ 3

Если вы включите метаданные Rails в заголовок HTML с <%= csrf_meta_tags %> это сгенерирует следующее.

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

Вы можете извлечь токен CRSF из метаданных и передать его в свой асинхронный запрос. Используя собственный метод js fetch вы можете передать его как заголовок x-csrf-token.

Это урезанный обработчик onSave для компонента React, который расширяет стандартную форму Rails.

  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', '~> 5.0.5' & "react": "^16.8.6",

Ответ 4

некоторые комментаторы, в частности ihaztehcodez, упоминают, что метод skip_before_action :verify_authenticity_token не настолько безопасен, потому что вы теряете защиту от подделки.

они упоминают, что здесь упоминаются лучшие/безопасные/"лучшие практики", решения. ПРЕДУПРЕЖДЕНИЕ: Не удается проверить рейки аутентификации токена CSRF.

например

$.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')
},