如何在reCAPTCHA验证之前处理HTML表单验证?

37

我集成了新的隐藏式reCAPTCHA (v2)框架,该框架默认使用提交按钮的点击事件来验证用户。但是,这个事件会在内置的HTML表单验证之前触发。我正在寻找一种使其按照预期顺序进行的方法:先进行表单验证,然后再进行reCAPTCHA验证。

8个回答

45

由于新的v2 grecaptcha方法,您必须以编程方式完成它:grecaptcha.execute(),以便reCAPTCHA不会替换按钮的默认单击事件,这会阻止默认的HTML5表单验证。

事件路径如下:

  1. 提交按钮单击事件:浏览器内置表单验证
  2. 表单提交事件:调用grecaptcha.execute()
  3. reCAPTCHA回调:提交表单

$('#form-contact').submit(function (event) {
    event.preventDefault();
    grecaptcha.reset();
    grecaptcha.execute();
  });

function formSubmit(response) {
  // submit the form which now includes a g-recaptcha-response input
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.google.com/recaptcha/api.js"></script>
<form action="?">
  <div class="g-recaptcha" 
       data-sitekey="your-key"
       data-size="invisible"
       data-callback="formSubmit">
  </div>
  <button type="submit">Submit</button>
</form>


4
使用这段代码,表单将永远不会提交,因为它正在阻止默认操作(即提交)。如果您想要提交,请勿基于条件阻止操作,就像下面的答案一样,请在那里检查。 - giovannipds
1
@giovannipds 这段代码正在我们的网站上运行,它会在 reCAPTCHA 表单测试正确完成后自动提交 grecaptcha.execute() - Julio Guerra
2
@JulioGuerra 你是对的,但它不幸没有使用浏览器默认验证。我想我的下面的代码会这样做。试着去检查一下吧。 - giovannipds
3
formSubmit()需要为空吗? - Black
2
@Black 不一定。文档中说:“用户的响应,g-recaptcha-response,将成为您回调函数的输入。”我修改了示例以包括它,而不是忽略它(因为您还可以在验证后从表单中的隐藏输入中读取它)。 - Julio Guerra
显示剩余8条评论

6

Hi got a working solution here. Working with invisible Recaptcha.

jQuery(document).ready(function() {
    var commentform = jQuery("#commentform");
    commentform.on("click", "#submit-comment", function(e) {
      if(commentform[0].checkValidity()) {
        e.preventDefault();
        grecaptcha.execute();
      }
    });
});

function submitCommentForm(data) {
    document.getElementById("commentform").submit();
}
<form action="blaba.php" method="post" id="commentform" class="comment-form">
  <div class="form-submit">
    <div data-callback="submitCommentForm" data-sitekey="yourkey" class="g-recaptcha" data-size="invisible">
    <button id="submit-comment">Leave a comment</button>
  </div>
</form>


6
以下是我对获取HTML5验证和隐形验证码的解决方案: HTML:
<form id="my-form">
    <!-- Your form fields ... -->
    <div class="g-recaptcha"
        data-sitekey="..."
        data-callback="submitMyForm"
        data-size="invisible">
    </div>
    <button type="submit">Submit</button>
</form>

JS:

var myForm = $('my-form');

function submitMyForm () {
    myForm.trigger('submit', [true]);
}

$(function () {
    myForm.on('submit', function (e, skipRecaptcha) {
        if(skipRecaptcha) {
            return;
        }

        e.preventDefault();
        grecaptcha.execute();
    });
  })

3
这是我的解决方案。
  • 使用reCaptcha v3(隐形)文档
  • 使用本地HTML5表单验证
  • 使用纯JS
  • 使用标准POST处理(可以修改为AJAX)

添加所需的任意数量的表单,只需更改两个位置中的“UNIQUE_FORM_ID”,并更新表单的“POST_URL”。 确保在“RECAPTCHA_SITE_KEY”的位置使用您自己的密钥。

<form id="UNIQUE_FORM_ID" method="post" action="POST_URL">
    <!-- ** Notice ** this hidden input field that will later send our g-recaptcha token back to our server -->
    <input type="hidden" name="g-recaptcha-response" value="">
    <!-- Add other hidden nonce fields -->

    <!-- Required field -->
    <input name="fullname" type="text" placeholder="Full Name" required>

    <!-- Submit button -->
    <!-- ** Notice ** the 'form' attribute; using SAME value as it's parent's form id, above. -->
    <!-- ** Notice ** the 'onclick' attribute; be sure to pass event -->
    <button type="submit" form="UNIQUE_FORM_ID" onclick="formSubmitBtn(event)">Send</button>
</form>

<!-- Only add scripts once -->
<!-- ** Notice ** to manually call grecaptcha, our site key must be included when loading api.js using the 'render' query param -->
<script src="https://www.google.com/recaptcha/api.js?render=RECAPTCHA_SITE_KEY"></script>
<script>
    /**
     * Handles form submissions for Google recaptcha v3.
     * Allows for HTML5 form validation to complete before processing.
     */
    function formSubmitBtn($event) {
        /**
         * Checks the validity of the form.
         * Return if invalid; HTML5 validation errors should display.
         */
        if (!$event.target.form.checkValidity()) {
            return;
        }
        /**
         * Form is client-side valid; taking over the remainder of processing.
         */
        $event.preventDefault();
        grecaptcha.ready(function() {
            grecaptcha.execute("RECAPTCHA_SITE_KEY", { action: 'submit' }).then(function(token) {
                /**
                 * Adds the token g-recaptcha-response token to our hidden form element.
                 * ** Notice ** we our referencing the specific form's input element by name here (do not use IDs).
                 */
                $event.target.form.elements['g-recaptcha-response'].value = token;
                /**
                 * Use the form API directly to submit the form.
                 */
                $event.target.form.submit();
            });
        });
    }
</script>

我偶然看到了你的帖子,寻找类似的解决方案。这个方案是纯客户端吗?令牌似乎没有被填充。其次,你选择不使用 addEventListener 的原因是什么?例如:document.addEventListener('DOMContentLoaded', function () { document.getElementById('html-submitt') .addEventListener('submit', formSubmitBtn); }); - Motivated
这是为了灵活性而设计的。如果您问的是服务器依赖性,那么没有。当然,您需要使用您的私有API密钥将响应令牌发送到Google以获取reCAPTCHA值。您是否用您特定的密钥替换了“RECAPTCHA_SITE_KEY”?我在生产环境中运行此代码,它对我有效。我不认为有必要添加事件监听器并消耗资源,并冒着因未删除事件监听器而导致的内存泄漏等风险(根据使用情况而定)。一个函数将支持无限表单。很乐意提供进一步帮助。代码片段? - PigBoT
代码正在使用更新的recaptcha密钥完全运行。当表单被提交时,它执行本地HTML5验证(这是一个加分项)。然后捕获值,如名称。但是它不会在隐藏输入中返回令牌。它是空的。使用事件侦听器的原因是因为内联点击事件被内容安全策略阻止。 - Motivated
我不是CSP专家。有很多问题,但这些评论不是讨论的地方。:P 对于你的情况,这听起来像一个合理的解决方法。在更改过程中可能会出现很多问题,特别是在$event周围。它仍然按预期传递吗?token是从execute方法创建的吗?它是否将token值附加到正确的表单上?如果您制作一个CodePen或类似的东西,我可以帮助调试。 - PigBoT
我不确定Codepen或类似的平台是否有实现CSP策略的选项,因为这通常是在服务器端完成的。如果您愿意,我很乐意分享内容安全策略。您的意思是它会在加载时出现吗?如果是,那么没有令牌在加载时生成。当表单提交时也不会生成令牌。隐藏值为空。 - Motivated
执行方法应在提交表单之后才被调用。因此,如果在尝试提交有效表单后,在执行期间记录令牌的控制台不会记录,则说明与 API 密钥设置或调用 formSubmitBtn 方法的方式有关的其他问题存在。您是否在 Google 应用程序 API 中列入白名单的域?您是否在控制台中收到任何其他错误? - PigBoT

2

作为默认方法似乎会覆盖HTML5表单验证,我遇到了这个问题。我还希望所有代码都是通用的,而不是硬编码任何函数/元素名称。最后,我使用v3 api想出了以下代码 -

HTML

最初的回答:

<form method="post" action="?" class="ui-recaptcha" name="my_form_name">
   ...
   <input type="submit" value="Submit">
</form>
<script src="//www.google.com/recaptcha/api.js?render={key}" async defer></script>

JavaScript(我正在使用jQuery,但很容易适应原始JS)

最初的回答:

$('.ui-recaptcha').submit(e => {

    var form = e.target;

    if( $(form).data('recaptcha-done') )
        return;

    e.preventDefault();
    grecaptcha.execute('{key}', {'action': $(form).attr('name')}).then(token => {

        $(form).append($('<input>').attr({'type': 'hidden', 'name': 'g-recaptcha-response', 'value': token}));
        $(form).data('recaptcha-done', true);
        $(form).submit();
    });
});

我发现,像上面一些示例中所述的仅调用submit会导致一个循环,这是有道理的,因为reCAPTCHA处理程序在submit事件上运行。
这将为任何ui-recaptcha表单运行reCAPTCHA,将表单的name属性作为操作传递到reCAPTCHA控制台中,然后将令牌插入表单。运行一次后,它会在表单上设置一个数据属性,以便递归调用提交时不会再次运行reCAPTCHA。
最初的回答

1

这个解决方案与@PigBoT的解决方案类似,但增加了reportValidity()并使用ReCAPTCHA v3

致谢https://github.com/ambethia/recaptcha/issues/302#issuecomment-621794131

<script src="https://www.google.com/recaptcha/api.js"></script>
<script type="text/javascript">
    function contactOnSubmit(token) {
        var contactForm = document.getElementById('contactUs');
        if(contactForm.checkValidity()) {
            //SERVER SIDE VALIDATION here, 
            //on success, contactForm.submit();  
        } else {
            grecaptcha.reset();
            contactForm.reportValidity();
        } 
    }
</script>

表单(id="contactUs")

<button class="g-recaptcha" data-sitekey="..." data-callback="contactOnSubmit" data-action="submit">Submit</button>

"Can I Use" 网站目前报告 97% 的使用情况支持 checkValidity() https://caniuse.com/?search=checkValidity


0
 let siteKey = "...";
 $("form").submit(function (eventObj) {
        var myForm = this;
        eventObj.preventDefault();
        grecaptcha.execute( siteKey, {
            action: "submit"
        })
            .then(function (token) {
                $('<input />').attr('type', 'hidden')
                    .attr('name', "g_recaptcha_response")
                    .attr('value', token)
                    .appendTo(myForm);
                myForm.submit();
            });
    });

这将执行reCAPTCHA,等待响应,在浏览器尝试提交表单时向任何表单添加隐藏属性g_recaptcha_response,然后实际提交它。您需要全局变量siteKey。


0

我想要相同的行为,但是使用新的reCAPTCHA,即隐形的reCAPTCHA。经过查看一些代码和测试一些东西,我进入了这个领域。主要区别在于这个也使用默认的浏览器验证

var contact_form;
$(function() {
    contact_form = $('#contact-form');
    contact_form.submit(function (event) {
        if ( ! contact_form.data('passed')) {
            event.preventDefault();
            grecaptcha.execute();
        }
    });
});
function sendContactForm(token) {
    contact_form.data('passed', true);
    contact_form.submit();
}

它基本上将jquery表单对象存储在全局变量中,包括使用sendContactForm作为回调,但是当由recaptcha调用时,它设置了一个名为passed的数据变量,这允许表单不被阻止。这与recaptcha通常所做的完全相同,但有这个条件。

更新:重新审视我的代码提醒我,它可能需要一种在grecaptcha执行后恢复传递给false的数据的方法。如果您要实施此功能,请考虑这一点。


exactly the same flow - Julio Guerra
不,如果我没记错的话,你上面的答案没有使用浏览器验证默认值,而我的答案使用了。在离开之前请考虑测试两者。谢谢。 - giovannipds

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接