如何在 SvelteKit 联系表单中获取 recaptcha 令牌

How to get recaptcha token in SvelteKit contact form

我正在使用 SvelteKit 开发一个静态网站来获得我的第一个 svelte 方法。我几乎让一切都按预期工作。 除了联系人表单上的重新验证保护之外的所有内容。

我可以获得 recaptcha 令牌,但在将它传递给 formData 请求时我丢失了它。 我的相关代码是:

<script lang="ts">
    import * as yup from "yup";
    import {createForm, ErrorMessage} from "svelte-forms-lib";
    ...

    function getCaptcha(){
        grecaptcha.ready(function () {
            grecaptcha.execute('recaptcha-key', { action: 'contactForm' })
                .then(function (token) {

                    console.log(token) // here I get the token
                    return token;
                });
        });
    }

    const  sendMail= (values)=>
    {
        const url = '/sendmail.php';

    const formData = new FormData();
    let token = getCaptcha();
    console.log('token: '+token); // here token is null

    formData.append("token", token);

    fetch(url, {
        method: 'post',
    body: formData,
        })
    .then(function (response) {
        console.debug(response.status)
    })
    .catch(function (e) {
        sendError = true;
    console.debug(e);
        });
    }
</script>

表单提交工作正常,除了 recaptcha 令牌。

感谢您提供解决方法。

token 为空,因为您的 getCaptcha 函数实际上并未返回任何内容,因为您正在使用回调来获取令牌。

function getCaptcha(){  // <- this function isn't returning anything
    grecaptcha.ready(function () {
        grecaptcha.execute('recaptcha-key', { action: 'contactForm' })
            .then(function (token) {  // <- this function returns, but the value isn't going anywhere
                console.log(token);
                return token;
            });
    });
}

要解决此问题,您可能需要研究使用 async/await 语法来实现承诺。 MDN has a great article如果你有兴趣。

你可能想做这样的事情:

async function getCaptcha(){
    await new Promise((resolve, reject) => {
        // grecaptcha.ready needs a callback so we create a promise to await
        grecaptcha.ready(resolve);
    });
    // grecaptcha.execute returns a promise so we can await it
    const token = await grecaptcha.execute('recaptcha-key', { action: 'contactForm' });
    console.log(token);
    return token;
}

const sendMail = async (values) => {
    const url = '/sendmail.php';

    const formData = new FormData();

    let token = await getCaptcha();
    console.log('token: ' + token);

    formData.append("token", token);

    // you have a few options for converting
    // the Promise.catch()
    // you can use a try {} catch {} block
    // or you can use .catch() on this sendMail function
    try {
        const response = fetch(url, {
            method: 'post',
            body: formData,
        });
        console.debug(response.status);
    } catch (e) {
        sendError = true;
        console.debug(e);
    }
}

请记住,这将使 sendMail 函数成为 async 函数,这意味着您将不得不 await 调用它,或使用 sendMail().then()。我相信也可以直接在同步代码中调用 async 函数并让它们 运行,只是不能保证函数中的内容会 运行 在调用后的代码之前.

为了澄清起见,我的最终代码在提交功能上略有不同,我将其发布以供将来参考,以防万一。

async function getCaptcha(){
        await new Promise((resolve, reject) => {
            // grecaptcha.ready needs a callback so we create a promise to await
            grecaptcha.ready(resolve);
        });
        // grecaptcha.execute returns a promise so we can await it
        const token = await grecaptcha.execute('site-key', { action: 'contactForm' });
        return token;
    }

    const sendMail = async (values) => {
        const url = '/sendmail.php';

        const formData = new FormData();

        let token = await getCaptcha();

        formData.append("name", values.name);
        formData.append("surname", values.surname);
        formData.append("email", values.email);
        formData.append("phone", values.phone);
        formData.append("message", values.message);
        formData.append("token", token);
        formData.append("action", "contactForm");


        // you have a few options for converting
        // the Promise.catch()
        // you can use a try {} catch {} block
        // or you can use .catch() on this sendMail function
        try {
            let response = await fetch(url, {
                method: 'post',
                body: formData,
            });

            if(response.status == 202){
                sendSuccess = true;
                document.querySelector('form').reset();
            }
            if(response.status == 400){
                sendError = true;
            }
        } catch(err) {
            sendError = true;
        }
    }

您可以使用 svelte-recaptcha-v2.

而不是自己处理

存储库

https://github.com/basaran/svelte-recaptcha-v2

演示

https://basaran.github.io/svelte-recaptcha-v2/demo

P. S 我是包的作者。