【问题标题】:How to validate my controller action was called from within my Rails app如何验证我的控制器操作是从我的 Rails 应用程序中调用的
【发布时间】:2016-01-14 22:57:55
【问题描述】:

我有一个简单的用户注册表单。

当用户填写表单并单击“提交”时,我有一个 JavaScript 事件,它拦截提交并使用 AJAX 调用下面的validate 方法以在提交前检查表单数据。如果表单数据正常,则继续提交,但如果不是,则取消提交并在页面上显示警告。这是一种进行实时客户端验证的简单方法。

# app/controllers/users_controller.rb

# Validates the form data. Returns an error message response in the format
#   { response: "Invalid email format!" }
#
# A blank response means success
#
def validate
  if request.xhr? || <request came from another method in this controller>
    # Run various validations on input data and determine a response
    # response_text = ...
    render json: { response: response_text }
  else
    redirect_to root_path
  end
end

def create
  if JSON.parse(validate)["response"].blank?
    User.create(...)
    # Other stuff
  end
end

但是,当提交最终通过并通过时,它会向create 操作发送POST 以创建新的User。该方法调用validate再次(重新验证的成本很小)以确保没有人绕过表单并直接向服务器提交恶意请求。

所以我的validate 方法必须响应应用程序内的 (a) AJAX 调用和 (b) “内部”调用。对 validate 操作的所有其他调用都应该重定向到根路径。

我可以使用request.xhr? 非常简单地判断调用是否是 AJAX 调用。

  1. 如何检查操作是否是在内部调用的,而不是由用户调用的?

  2. 退一步说,这是一种很好的通用验证方法吗?有什么改进的想法吗?

谢谢!

【问题讨论】:

    标签: ruby-on-rails ajax validation


    【解决方案1】:

    每当用户查看表单并将其作为随机字符串存储在 HTML 中时,Rails 都会生成一个authenticity token。该令牌也存储在会话中,因此对用户不可见。收到请求后,您的应用程序将比较令牌以验证请求是否是从您的表单生成的。

    TL;DR:别担心,您已经受到保护。

    【讨论】:

    • 很高兴知道 - 但在这种情况下,我可以直接绕过表单和 POST 对我的应用程序的请求,该请求转到 create。如图所示,它将尝试调用validate,但由于它不是AJAX 请求,validate 方法甚至不会验证任何内容。我可以删除 if request.xhr? 的要求,但我想看看是否有办法将调用此方法的内容显式列入白名单,因为用户也可以通过公共路由访问它
    • @user2490003 我想这就是我对您的要求感到困惑的地方。如果您已经有了针对 CSRF 的保护,那么您的客户端验证是否为您提供了任何实用程序?显式控制真的有必要吗?
    【解决方案2】:

    我没有具体的答案,但确实有一些信息可以提供帮助。如果需要,我很乐意删除它...


    AJAX 调用和“内部”调用

    您必须了解 XHR 请求只能来自您的应用 -- CORS (cross origin resource sharing)

    Ajax 调用只能来自您自己的域,所以不要认为恶意黑客可以运行一些抓取工具或其他任何东西 -- you choose 允许哪些域发送 XHR 请求。

    所以当你打电话时......

    if request.xhr?
    

    ...作为验证的一部分,您需要确定何时使用 XHR。


    同上,您正在执行什么验证?

    如果您要验证输入数据,Rails 会在模型层进行处理。我们已经完成something similar(点击顶部登录/注册):

    Rails 的基本结构是MVC,这意味着您的controller 只负责接受请求并从中构建适当的数据对象。

    因此,如果您要“验证”输入,除了数据本身(由模型处理)之外,还有什么要验证的?

    正如@MarsAtomic 所指定的那样,Rails 也使用 CSRF 令牌为表单提交提供某种级别的身份验证。

    --

    您可以轻松使用以下内容:

    #app/controllers/users_controller.rb
    class UsersController < ApplicationController
       respond_to :js, :json, :html
       def create
          @user = User.new user_params
          respond_with @user.save
       end
    end
    

    如果您随后以 json 的形式从前端发送请求,您将返回创建的用户或错误:

    #app/views/users/new.html.erb
    <%= form_for @user, remote: true, format: :json do |f| %>
       <%= f.text_field .... %>
       <%= f.submit %>
    <% end %>
    
    #app/assets/javascripts/application.js
    $(document).on("ajax:error", "#new_user", function(xhr, status, error) {
        // do stuff with response
    });
    

    【讨论】:

    • 谢谢,这是有用的信息!
    【解决方案3】:

    在您的 create 方法中,如果 @user 无效(如果他们跳过 ajax 验证),您希望返回它。

    如果是这种情况,你不想做User.create,而是创建一个实例并尝试保存它。如果您执行所有这些操作,那么您正在执行创建的正常控制器操作,并且无需在内部调用 validate。

    def create
      @user = User.new(user_params)
      if @user.save
        # Other stuff
      end
    end
    

    而且,创建 json 响应并对其进行解析以查看对象是否有效并不是一个好习惯。相反,要么测试有效吗?或者只是尝试保存并取回真假。

    换句话说 - 将您的 validate 方法与您的 create 方法分开。他们最终都会依赖valid?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-24
      • 1970-01-01
      • 1970-01-01
      • 2021-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多