Estoy desarrollando un sitio web estático usando SvelteKit para obtener mi primer acercamiento a svelte. Tengo casi todo funcionando según lo previsto. Todo menos la protección de recaptcha en el formulario de contacto.
Puedo obtener el token de recaptcha pero lo estoy perdiendo al pasarlo a la solicitud formData. Mi código relativo es:
<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>El envío de formularios funciona bien, excepto, obviamente, para el token de recaptcha.
Gracias por cualquier idea sobre cómo solucionarlo.
token es nulo porque su función getCaptcha en realidad no devuelve nada, ya que está utilizando devoluciones de llamada para obtener el token.
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; }); }); } Para solucionar esto, es posible que desee considerar el uso de la sintaxis async / await para las promesas. MDN tiene un gran artículo si estás interesado.
Probablemente quieras hacer algo como esto:
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); } } Tenga en cuenta que esto hará que la función sendMail async , lo que significa que tendrá que await llamadas o usar sendMail().then() . Creo que también es posible llamar a funciones async directamente en código síncrono y hacer que se ejecuten, simplemente no está garantizado que las cosas en la función se ejecuten antes que el código que viene después de la llamada.
Mi código final, para aclaración, es ligeramente diferente en la función de envío, y lo estoy publicando para referencia futura, por si acaso.
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; } }En lugar de manejar esto usted mismo, puede usar svelte-recaptcha-v2 .
https://github.com/basaran/svelte-recaptcha-v2
https://basaran.github.io/svelte-recaptcha-v2/demo
P. S Soy el autor del paquete.