BlogContacts
BlogGamesContacts

{{item}}

Simple feedback form

30.04.2020

Java, Spring, AngularJS, Material

DemoSee GitHub
External feedback form for a static site. This application includes a backend and frontend, so that it can be called both from an external site and from the current site. Frontend has a responsive layout for both desktop computers and mobile devices.
The backend uses java mail-sender library for messaging and provides the simple-captcha generation and checking. Three methods are available from frontend: captcha, checkCaptcha and finally sendFeedback, if two previous are passed.
The frontend is expected to use an ajax calls of the backend POST methods. This application uses the AngularJS http service. These calls require configuring of cross origin requests processing on both the backend and frontend.The expected algorithm of user's actions is as follows: first he has to fill the required fields (name, email and message), optionally he may want to add his website address and attach several files. After that, a field with a captcha image becomes available. The browser sends a GET request to the server's captcha endpoint. The server opens a session for this client with a 5 minutes timeout. When user enters the characters from the picture and clicks the send button, the browser sends the first POST request to checkCaptcha and, if it is passed, sends the second POST request to sendFeedback.There are described the main steps to configure this application. The entire code is available on GitHub. The demo version is available on Heroku.Example of the incoming message:
Simple feedback form

Configure the application

application.properties - is the main configuration file. It must be present in the resources folder of this application, or in the user's home folder. This is a template file:
# Rename this file to the 'application.properties'

# Temp directory for attachments
temp.folder=/tmp

# Comma-separated domain names,
# where this application runs
# and where it is called from
allowed.origins=https://awesomeapp.herokuapp.com/,https://domain2.com

# Mail server properties
mail.smtp.host=smtp.mail.ru
mail.smtp.ssl.trust=smtp.mail.ru
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
mail.smtp.auth=true
mail.smtp.socketFactory.port=465
mail.smtp.port=465

# Letter box name and password
mail.smtp.username=some_letterbox@mail.ru
mail.smtp.password=ReallyToughPassword111!
You can download precompiled web application archive, or build application yourself. If so, pay attention to the required Maven dependencies. These libraries should be built and installed into local Maven repository (.m2 folder by default) before building this application:
XML
<dependency>
    <groupId>org.drakonoved</groupId>
    <artifactId>mail-sender</artifactId>
    <version>1.0.4</version>
</dependency>

<dependency>
    <groupId>org.drakonoved.cipher</groupId>
    <artifactId>simple-captcha</artifactId>
    <version>1.0.5</version>
</dependency>

Deploy to heroku

After the war file is build (or downloaded), it can be deployed to some server, such as heroku for example. To do that, first download heroku-cli. Then use terminal:
// check heroku-cli is downloaded successfully
$ heroku --version
// heroku/7.39.5 linux-x64 node-v12.16.2

// install java plugin
$ heroku plugins:install java
// Installing plugin java... installed v3.1.1

// deploy <war file> to <application name>
$ heroku war:deploy simple-feedback-form.war --app awesomeapp --jdk 14 --includes application.properties
// Uploading simple-feedback-form.war
// -----> Packaging application...
// - app: awesomeapp
// - including: application.properties
// - including: webapp-runner.jar
// - including: simple-feedback-form.war
// -----> Creating build...
// - file: slug.tgz
// - size: 30MB
// -----> Uploading build...
// - success
// -----> Deploying...
// remote:
// remote: -----> heroku-deploy app detected
// remote: -----> Installing JDK 14... done
// remote: -----> Discovering process types
// remote:        Procfile declares types -> web
// remote:
// remote: -----> Compressing...
// remote:        Done: 96.9M
// remote: -----> Launching...
// remote:        Released v3
// remote:        https://awesomeapp.herokuapp.com/ deployed to Heroku
// remote:
// -----> Done
For more information about heroku-cli:
$ heroku --help
$ heroku war:deploy --help

Configure cross origin requests processing and POST requests

If you do not plan to make changes to the backend or frontend, you may skip this section, otherwise pay attention to these points:
JavaScript
angular.module("application")

.config(['$httpProvider', function ($httpProvider) {
    $httpProvider.defaults.withCredentials = true;
}]);
Java
@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
    @Value("${allowed.origins}")
    private String allowedOrigins;

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/*").allowCredentials(true)
                .allowedOrigins(allowedOrigins.split(","));
    }
}
AngularJS http service POST requests - the first to checkCaptcha and the second to sendFeedback:
JavaScript
scope.formFields = {
    userName: '',
    email: '',
    website: '',
    messageText: '',
    captchaText: '',
};
scope.sendingData = false;
scope.sendFeedback = function() {
    if (!scope.feedbackForm.$valid) return;

    scope.sendingData = true;

    http({
        url: '/checkCaptcha',
        method: "POST",
        params: {captchaText: scope.formFields.captchaText},
        headers: {'Content-Type': undefined },
    }).then(function doSendFeedback(response) {
        http({
            url: '/sendFeedback',
            method: "POST",
            params: scope.formFields,
            headers: {'Content-Type': undefined },
            data: formData,
        }).then((response) => successCallback(response),
            (response) => errorCallback(response));
        }, (response) => errorCallback(response));

    let successCallback = function(response) {
        scope.formFields.captchaText = '';
        scope.feedbackForm.captchaText.$setPristine();
        scope.feedbackForm.captchaText.$setUntouched();
        document.querySelector(".reloadButton").click();
        scope.sendingData = false;
    };
    let errorCallback = function(response) {
        scope.formFields.captchaText = '';
        scope.feedbackForm.captchaText.$setDirty();
        document.querySelector(".reloadButton").click();
        scope.sendingData = false;
    };
};
Privacy policy
Back to Top