Hello, Habr.
Below you will find a translation dedicated to the development of web applications. The stack described by the author demonstrates an interesting possibility of combining Java and JavaScript, and also allows you to take a fresh look at creating one-page web applications.
In this case, we will ask if you want to see on the shelf the translation of the following books on
Spring and
AngularJS')


The technologies Spring MVC and AngularJs together form a truly productive and attractive stack for developing web applications, especially those that require intensive work with forms.
This article will discuss how to build just such an application. We will compare this approach with other available options. A full-featured secure web application written using Spring MVC / AngularJs is located in this
repository on GitHub.
We will consider the following questions:
- Architecture of the one-page web application Spring MVC + Angular
- How to structure a web user interface with Angular
- Which JavaScript / CSS libraries work well with Angular
- How to build a machine interface REST API using Spring MVC
- Protecting the REST API with Spring Security
- Comparing this approach with others, where the entire project is implemented in Java?
Architecture of the one-page web application Spring MVC + AngularApplications for the corporate environment, involving intensive work with forms, are best done in the form of single-page web applications. The main idea that makes them stand out against the background of more traditional server architectures is to create a server as a set of stateless, reusable REST services. In the context of MVC, it is important to remove the controller from the machine interface and transfer it to the browser:

The client supports the MVC pattern and contains all the presentation logic, which is divided into the presentation level, the controller level and the client services level. When the application is launched, the client and server will only exchange JSON data.
How is the machine interface implemented?The machine interface of a corporate application with a client part is logical and convenient to write as a REST API. The same technology can be used to provide web services to third-party applications. Often, in such cases, there is no need for a separate stack of SOAP web services.
From the point of view of
DDD, the domain model remains on the machine interface, in the same place where the level of services and the level of persistence are. Only
DTOs are transmitted over the network, but not the domain model.
How to structure the client part of a web application using AngularThe client part should line up around the model related to the view (and not the subject area). Here only the presentation logic should be processed, but not the business logic. In the client part, there are three levels:
Presentation levelThe presentation layer consists of HTML templates, CSS tables, and Angular directives, corresponding to the various components of the user interface. Here is an example of a simple presentation for a
login form :
<form ng-submit="onLogin()" name="form" novalidate="" ng-controller="LoginCtrl"> <fieldset> <legend>Log In</legend> <div class="form-field"> <input type="text" ng-model="vm.username" name="username" required="" ng-minlength="6"> <div class="form-field"> <input type="password" ng-model="vm.password" name="password" required="" ng-minlength="6" pattern="(?=.*\d)(?=.*[az])(?=.*[AZ]).{6,}"> </div></div></fieldset> <button type="submit">Log In</button> <a href="/resources/public/new-user.html">New user?</a> </form>
Control level (controller)The control level consists of Angular controllers, sticking data together, retrieved respectively from the machine interface and from the view. The controller initializes the view model and determines how the view should respond to model changes and vice versa:
angular.module('loginApp', ['common', 'editableTableWidgets']) .controller('LoginCtrl', function ($scope, LoginService) { $scope.onLogin = function () { console.log('Attempting login with username ' + $scope.vm.username + ' and password ' + $scope.vm.password); if ($scope.form.$invalid) { return; } LoginService.login($scope.vm.userName, $scope.vm.password); }; });
One of the main tasks of the controller is to perform validation in the client part. All such client validations are provided only for the convenience of the user - for example, with their help, it is convenient to immediately inform the user that the field is required.
All acts of client validation must be repeated on the machine interface (at the service level) for security reasons, since client validation is easy to bypass.
Client Services LevelIn Angular controllers, you can embed a set of Angular services that can interact with the machine interface:
angular.module('frontendServices', []) .service('UserService', ['$http','$q', function($http, $q) { return { getUserInfo: function() { var deferred = $q.defer(); $http.get('/user') .then(function (response) { if (response.status == 200) { deferred.resolve(response.data); } else { deferred.reject('Error retrieving user info'); } }); return deferred.promise; }
Let's look at what other libraries we need to start the work of the client part.
Which JavaScript / CSS libraries should complement Angular?Angular already provides much of the functionality needed to create the client side of your application. Here are some interesting additions for Angular:
- PureCSS library from yahoo. It is written in pure CSS, provides a convenient design using the themes contained in it and weighs only 4k. Its Skin Builder component makes it easy to generate a theme based on the base color. This is a BYOJ (Bring Your Own Javascript) solution to help write code “in the spirit of Angular”.
- Library for data operations in the style of functional programming. This library seems to boast excellent support and documentation that has been unsurpassed since the days of lodash .
Armed with these two libraries and Angular, you can build almost any application with forms, practically nothing is needed anymore. Depending on the specifics of your project, some other libraries may be useful:
- It is convenient to have a system of modules like requirejs , but since the Angular module system does not handle file retrieval, there is some duplication when declaring requirejs dependencies and angular modules.
- Angular CSRF module, which prevents attacks related to cross-site request forgery.
- Internationalization module
How to build a machine interface REST API using Spring MVCThis machine interface contains the usual levels:
- Routing Level: Determines which service entry points correspond to specific HTTP URLs and how parameters will be read from an HTTP request.
- Service level: contains only business logic (for example, provides validation), determines the scope of business transactions
- Persistence level: Displays a database of domain objects stored in memory and vice versa.
Currently, the best Spring MVC configuration involves only using the Java configuration. Even
web.xml
web.xml
already, in general, is not required. See
here an example of a fully configured application that uses only Java.
Service levels and persistence are created using the normal DDD model, so let's pay attention to the routing level.
Routing levelThe same Spring MVC annotations that are used to create a JSP / Thymeleaf application can also be used in developing the REST API.
The big difference is that the controller's methods do not return a String object that would determine which view template to display. Instead, the @ ResponseBody annotation is applied, indicating that the return value of the controller method should be directly displayed and become the body of the response:
@ResponseBody @ResponseStatus(HttpStatus.OK) @RequestMapping(method = RequestMethod.GET) public UserInfoDTO getUserInfo(Principal principal) { User user = userService.findUserByUsername(principal.getName()); Long todaysCalories = userService.findTodaysCaloriesForUser(principal.getName()); return user != null ? new UserInfoDTO(user.getUsername(), user.getMaxCaloriesPerDay(), todaysCalories) : null; }
If all class methods are to be annotated
@ResponseBody
@ResponseBody
it is better to provide the entire class with the
@RestController
annotation
@RestController
.
If you add the Jackson JSON library, the return value of the method will be converted directly to JSON without any further configuration. In addition, this value can be converted to XML or other formats, depending on the value of the HTTP
Accept
header.
Accept
specified by the client.
This shows a pair of controllers that have error handling configured.
How to protect the REST API with Spring SecurityThe REST API can be secured using the Spring Security Java configuration. In this case, it is advisable to use the login form with HTTP Basic authentication as a backup option, as well as connect the protection from CSRF and the ability to hard to specify that all methods of the machine interface can only be accessed via HTTPS.
Thus, the machine will offer the user a login form, and after successful login, will assign the session cookie to browser clients, but will also work with other clients, supporting rollback to regular HTTP in cases where credentials are transmitted using the HTTP Authorization header.
In accordance with the recommendations of
OWASP, REST services can be programmed with minimal state preservation (all server state information is limited to the session cookie used for authentication). This is done so as not to send credentials over the network with each request.
Here is
an example of configuring REST API security:
http .authorizeRequests() .antMatchers("/resources/public/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .defaultSuccessUrl("/resources/calories-tracker.html") .loginProcessingUrl("/authenticate") .loginPage("/resources/public/login.html") .and() .httpBasic() .and() .logout() .logoutUrl("/logout"); if ("true".equals(System.getProperty("httpsOnly"))) { LOGGER.info("launching the application in HTTPS-only mode"); http.requiresChannel().anyRequest().requiresSecure(); }
This configuration takes into account authentication only in the security context, and the authorization strategy is chosen depending on the security requirements of the API. If you need fine control over authorization, check out the Spring Security ACLs access control lists and see if they are appropriate for your task.
Now let's compare this way of creating web applications with other common approaches.
Comparing Spring MVC / Angular stack with other common variantsThis way of using JavaScript in the client side and Java - working with a database simplifies the workflow and increases its productivity.
When the machine interface is already running, no special tools or plugins are required to disperse the hot deployment in the client side to full capacity: just publish resources on the server using the IDE (for example, press Ctrl + F10 in IntelliJ) and refresh the page in the browser.
Machine interface classes can still be reloaded using
JRebel , but in the client part, nothing needs to be done separately. In principle, you can build the entire client part, simulating the machine interface with, say,
json-server . In this case, various specialists will be able to develop in parallel the client part and the machine interface, if required.
Increased productivity or full-blown development?In my experience, the ability to directly edit HTML / CSS without any levels of indirection (see, for example, the
general comparison of Angular with GWT and JSF ) helps to reduce mental load and not complicate the work. The development cycle “edit-save-update” is very fast and reliable, it allows you to work much more productively.
The greatest gains in productivity are achieved in cases where the same developers write both the client side in JavaScript and the machine interface in Java, since for the implementation of most of the features, simultaneous changes are usually required both there and there.
The potential disadvantage of this approach is this: these developers need to know HTML, CSS, and JavaScript, but in recent years such competence is becoming more common.
My experience suggests that full-stack development allows you to implement the most difficult cases in the client side for a fraction of the time it takes to create a full-scale Java solution (days, not weeks), and this increase in productivity definitely justifies additional training.
findingsThe combination of Spring MVC and Angular allows you to really take a fresh look at the development of web applications related to intensive form filling. This approach increases productivity so much that you should definitely look at it.
The absence of a binding to the server state between requests (except for authentication with a cookie), by definition, relieves us of a whole class of bugs.
Additionally, I suggest to get acquainted with this application on
github .