Creating a Feedback Form for a Website on Cloud Functions

Feedback forms on the website can help you collect the reviews from customers and organize the acceptance of orders for services and products.

Let’s take a look at an example of the HTML page and a cloud function for sending the content of the feedback form to email:

  • creating a page with a feedback form;
  • adding spam protection with reCAPTCHA.

Simple Feedback Form

In this example, we will create a form asking the user for their name, phone number, and feedback, which they would like to share: a problem or an idea for the website.

The HTML of the feedback form will look as follows:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Contact Us Form Example</title>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
<body>
  <div class="content container pb-5">
    <div class="row justify-content-md-center mt-5 pl-5">
      <div class="col-8 mb-3">
        <form class="needs-validation" id="mainForm">
          <div class="form-group">
            <label for="name_field">Name</label>
            <input name="name" type="text" class="form-control" id="name_field"
                   value="Mark" required>
          </div>
          <div class="form-group">
            <label for="phone_field">Phone</label>
            <input name="phone" type="text" class="form-control" id="phone_field"
                   placeholder="+9 999 999999" value="+7 999 888777" required>
          </div>
          <div class="form-group">
            <label for="desc_field">Description</label>
            <textarea class="form-control" name="desc" id="desc_field" rows="3"></textarea>
          </div>
          <button class="btn btn-primary" type="submit">Submit</button>
        </form>
      </div>
    </div>
  </div>
  <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
  <script>
    jQuery(function() {
      $('#mainForm').on('submit', function(e) {
        e.preventDefault();
        $.ajax({
          url: '<Link to your cloud function>',
          type: 'POST',
          cache: false,
          data: $(e.target).serialize()
        }).done(function() {
          alert('Done.');
        }).fail(function() {
          alert('Something went wrong.');
        });
      });
    });
  </script>
</body>
</html>

You can find the above code in our github repository.

In the code, the «submit» event sends an AJAX POST request to the cloud function with the content of the web form. JQuery is enabled for this purpose, although there may be a different implementation of sending a POST request instead. Bootstrap4 is used to make the form look nice, but the design can be anything.

Let’s move on to the cloud function that is responsible for processing the received data.

The form contents can be saved in the DBMS or in the File Storage, sent to email, Telegram, Slack, your CRM system, and so on. For each request, a letter will be sent to the email specified in the function settings. This is the most common way to solve the problem.

The code for this function will look as follows:

import os
import json
import smtplib

EMAIL_HOST = os.environ.get('EMAIL_HOST', 'smtp.yandex.ru')
EMAIL_PORT = int(os.environ.get('EMAIL_PORT', 587))
EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')

EMAIL_TO = os.environ.get('EMAIL_TO')


def main(**kwargs):
    text = json.dumps(kwargs, indent=2, ensure_ascii=False)
    print("Received: %s" % text)

    server = smtplib.SMTP(EMAIL_HOST, EMAIL_PORT)
    server.ehlo()
    server.starttls()
    server.login(EMAIL_HOST_USER, EMAIL_HOST_PASSWORD)
    message = "\r\n".join([
        f"From: {EMAIL_HOST_USER}",
        f"To: {EMAIL_TO}",
        "Subject: Serverless Form",
        "",
        str(text)
    ])
    server.set_debuglevel(1)
    server.sendmail(EMAIL_HOST_USER, EMAIL_TO, message)
    server.quit()
    return "Email was sent"

You can find the above code in our github repository.

The code converts all received arguments into formatted JSON, connects to the SMTP server, and sends a message as text.

Here you need to set some settings in the environment variables, including for connecting to the SMTP server, for example, mail.google.com or mail.yandex.ru.

HTML form sends an AJAX request to the cloud function. At the same time, cloud function’s URL is «hidden» inside the Javascript code intentionally, so that trivial crawler bots cannot send spam using this form if they find the action attribute in it.

Feedback Form with a CAPTCHA

Let’s create a feedback form with a CAPTCHA to filter out any bots.

To add a CAPTCHA to the form, use the reCAPTCHA v3 service.

Follow these steps:

  1. Create a key in the google.com/recaptcha service.
  2. Convert HTML from the previous chapter into the following:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Contact Us Form Example</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    </head>
    <body>
    <div class="content container pb-5">
    <div class="row justify-content-md-center mt-5 pl-5">
      <div class="col-8 mb-3">
        <form class="needs-validation" id="mainForm">
          <div class="form-group">
            <label for="name_field">Name</label>
            <input name="name" type="text" class="form-control" id="name_field"
                   value="Mark" required>
          </div>
          <div class="form-group">
            <label for="phone_field">Phone</label>
            <input name="phone" type="text" class="form-control" id="phone_field"
                   placeholder="+9 999 999999" value="+7 999 888777" required>
          </div>
          <div class="form-group">
            <label for="desc_field">Description</label>
            <textarea class="form-control" name="desc" id="desc_field" rows="3"></textarea>
          </div>
          <button class="btn btn-primary g-recaptcha"
                  data-sitekey="<SITE KEY from reCAPTCHA>"
                  data-callback='onFeedbackFormSubmit'
                  data-action='submit'>Submit</button>
        </form>
      </div>
    </div>
    </div>
    <script src="https://www.google.com/recaptcha/api.js"></script>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <script>
    function onFeedbackFormSubmit() {
      $.ajax({
        url: '<Link to your cloud functionю>',
        type: 'POST',
        cache: false,
        data: $('#mainForm').serialize()
      }).done(function() {
        alert('Done.');
      }).fail(function() {
        alert('Something went wrong.');
      });
    }
    </script>
    </body>
    </html>

    You can find the above code in our github repository.

This will help you protect the cloud function call from crawler bots.

For complete protection, you need to validate the reCAPTCHA verification result from the server side. Follow these steps:

import os
import json
import smtplib
from urllib import request, parse

EMAIL_HOST = os.environ.get('EMAIL_HOST', 'smtp.yandex.ru')
EMAIL_PORT = int(os.environ.get('EMAIL_PORT', 587))
EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')

EMAIL_TO = os.environ.get('EMAIL_TO')

RECAPTCHA_SECRET_KEY = os.environ.get('RECAPTCHA_SECRET_KEY')

def is_captcha_challenge_succeed(response_token):
    data = parse.urlencode({
        'secret': RECAPTCHA_SECRET_KEY,
        'response': response_token,
    }).encode()
    req = request.Request('https://www.google.com/recaptcha/api/siteverify', data=data)
    resp_data = json.load(request.urlopen(req))
    return resp_data.get('success') or False

def format_email(**kwargs):
    return json.dumps(kwargs, indent=2, ensure_ascii=False)

def send_email(text):
    server = smtplib.SMTP(EMAIL_HOST, EMAIL_PORT)
    server.ehlo()
    server.starttls()
    server.login(EMAIL_HOST_USER, EMAIL_HOST_PASSWORD)
    message = "\r\n".join([
        f"From: {EMAIL_HOST_USER}",
        f"To: {EMAIL_TO}",
        "Subject: Serverless Form",
        "",
        str(text)
    ])
    server.set_debuglevel(1)
    server.sendmail(EMAIL_HOST_USER, EMAIL_TO, message)
    server.quit()

def main(**kwargs):
    if is_captcha_challenge_succeed(kwargs.pop('g-recaptcha-response')):
        text = format_email(**kwargs)
        print(f'Sending """{text}"""')
        send_email(text)
        return "Email was sent"
    return "CAPTCHA challenge failed"

You can find the above code in our github repository.

We have implemented a feedback form and provided full protection against spambots activity.