简体   繁体   中英

When submitting an ajax request, how can you “put the original request on hold” temporarily until a condition is met?

I am wanting to implement a recaptcha process that captures all ajax requests before they go through - the desired process would be as follows:

  1. User completes an action which is going to cause an ajax request of some sort.
  2. If the user has already completed the recaptcha process, the ajax request proceeds without further delay
  3. If the user has not completed the recaptcha process, put the ajax request "on hold" temporarily until the recaptcha process is completed, then continue the ajax request.

I have got things to a state where I intercept the call, however I don't know how to put it on hold temporarily. Here's the relevant code:

<script>
    var captchaValidated = null;

    var currentRequests = [];
    $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
        if (options.url != "/ValidateCaptcha") {
            if (captchaValidated == null || captchaValidated == false) {
                if (captchaValidated == null){
                    openRecaptcha();
                } else {
                    verifyCaptcha(); //see async question in method
                }
                if (!captchaValidated) {
                    jqXHR.abort();
                } else {
                    //let the original request proceed now - but how?!
                }
            }
        }
    });

    function verifyCaptcha() {
        var grecaptcha = $("g-recaptcha-response");
        var encodedResponse;
        if (grecaptcha != null) {
            encodedResponse = grecaptcha.val();
            $.ajax({
                async: false, //set to false so that the calling method completes rather than async - what do you think?
                headers: headers,
                cache: false,
                url: "/ValidateCaptcha",
                type: 'POST',
            contentType: 'application/json',
            success: function (data) {
                //parse the data - did we get back true?
                captchaValidated = data;
            },
            error: function (raw, textStatus, errorThrown) { captchaValidated = null; alert("Validate ReCaptcha Error: " + JSON.stringify(raw)); },
            data: JSON.stringify({ "encodedResponse": encodedResponse })
            });
        }
    }

    function invalidateCaptcha(){
        captchaValidated = null;
    }
    function openRecaptcha() {
        grecaptcha.render('recaptcha', {
            'sitekey': "thekey",
            'callback': verifyCaptcha,
            'expired-callback': invalidateCaptcha,
            'type': 'audio image'
        });
        $("#recaptchaModal").modal('show');
    }
</script>

Any suggestions of how to proceed would be appreciated, thanks in advance!

Thank you @Loading and @guest271314 for your help in pointing me in the right direction that helped me get things figured out. I've pasted how I accomplished it below - perhaps it will be of help to someone else. Of course if anyone would like to weigh in on my implementation please do.

<script src="https://www.google.com/recaptcha/api.js?onload=onloadCaptcha&render=explicit&hl=en" async defer></script>
<script>
    var captchaValidated = null;
    var currentRequests = [];
    var captchaPrompted = false;
    var captchaReady = false;
    var resetCaptcha = false;
    function onloadCaptcha() {
        captchaReady = true;
        captcha = grecaptcha.render('recaptcha', {
            'sitekey': '<yoursitekey>',
            'callback': verifyCaptcha,
            'expired-callback': invalidateCaptcha,
            'type': 'audio image'
        });
    }

    var deferredCaptcha = null;
    var promiseCaptcha = null;
    var captcha = null;
    function openRecaptcha() {
        if (!captchaReady) {
            setTimeout(openRecaptcha, 50);
        }
        if (captchaPrompted) {
            return;
        }
        captchaPrompted = true;
        var captchaTimer = setInterval(function () {
            if (captchaValidated != null) {
                if (captchaValidated) {
                    deferredCaptcha.resolve();
                } else {
                    deferredCaptcha.reject();
                    captchaValidated = null;
                }
            }
        }, 100);
        if (resetCaptcha) {
            captcha.reset();
        }
        deferredCaptcha = $.Deferred();
        promiseCaptcha = deferredCaptcha.promise();
        promiseCaptcha.done(function () {
            //captcha was successful
            clearInterval(captchaTimer);
            //process the queue if there's items to go through
            if (currentRequests.length > 0) {
                for (var i = 0; i < currentRequests.length; i++) {
                    //re-request the item
                    $.ajax(currentRequests[i]);
                }
            }
        });
        promiseCaptcha.fail(function () {
            //captcha failed
            clearInterval(captchaTimer);
            currentRequests = []; //clear the queue
        });
        $("#recaptchaModal").modal('show');
    }

    function verifyCaptcha() {
        resetCaptcha = true;
        var response = $("#g-recaptcha-response").val();
        var encodedResponse;
        // confirm its validity at the server end 
        $.ajax({
            headers: headers,
            cache: false,
            url: "/ValidateCaptcha",
            type: 'POST',
            contentType: 'application/json',
            success: function (data) {
                captchaValidated = data;
                if (!data) {
                    captchaPrompted = false;
                }
            },
            error: function (raw, textStatus, errorThrown) { captchaValidated = false; captchaPrompted = false; alert("WTF Validate ReCaptcha Error?!: " + JSON.stringify(raw)); },
            data: JSON.stringify({ "encodedResponse": response })
        });
    }
    function invalidateCaptcha(){
        deferredCaptcha.reject();
        captchaValidated = null;
        resetCaptcha = true;
    }

    $.ajaxSetup({
        beforeSend: function (xhr, settings) {
            if (settings.url == '/ValidateCaptcha' || captchaValidated) {
                // we're validating the captcha server side now or it's already been validated - let it through
            } else {
                if (typeof settings.nested === 'undefined'){
                    settings.nested = true; //this flag is to determine whether it's already in the queue
                    currentRequests.push(settings); //add the request to the queue to be resubmitted
                    //prompt them with the captcha
                    openRecaptcha();
                }
                return false; // cancel this request
            }
        }
    });
</script>

At $.ajaxPrefilter() use .then() chained to openCaptcha to call verifyCaptcha

if (captchaValidated == null){
                openRecaptcha().then(verifyCaptcha);
            }

at verifyCaptcha use .is() with parameter "*" to check if an element exists in document

if (grecaptcha.is("*")) {

at openRecaptcha() , if grecaptcha.render does not return asynchronous result return jQuery promise object using .promise() ; else chain to grecaptcha.render and $("#recaptchaModal").modal('show') ; using $.when()

return $("#recaptchaModal").modal('show').promise()

or

return $.when(grecaptcha.render(/* parameters */)
       , $("#recaptchaModal").modal('show').promise())

Something like this? (pseudo-code)

 verified = false; $('#myButton').click(function(){ if (!verified) verify_by_captcha(); if (verified){ $.ajax(function(){ type: 'post', url: 'path/to/ajax.php', data: your_data }) .done(function(recd){ //ajax completed, do what you need to do next alert(recd); }); } });//end myButton.click 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM