Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Spring 5.0 Projects

You're reading from   Spring 5.0 Projects Build seven web development projects with Spring MVC, Angular 6, JHipster, WebFlux, and Spring Boot 2

Arrow left icon
Product type Paperback
Published in Feb 2019
Publisher Packt
ISBN-13 9781788390415
Length 442 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Nilang Patel Nilang Patel
Author Profile Icon Nilang Patel
Nilang Patel
Arrow right icon
View More author details
Toc

Table of Contents (9) Chapters Close

Preface 1. Creating an Application to List World Countries with their GDP 2. Building a Reactive Web Application FREE CHAPTER 3. Blogpress - A Simple Blog Management System 4. Building a Central Authentication Server 5. An Application to View Countries and their GDP using JHipster 6. Creating an Online Bookstore 7. Task Management System Using Spring and Kotlin 8. Other Books You May Enjoy

Defining the API controllers

So far, we have written code to interact with the DB. Next up is to work on the code for the controller. We will have both types of controller—one that returns the view name (Thymeleaf template in our case) with the data for the view populated in the model object, and the other that exposes the RESTful APIs. We will need to add the following dependency to pom.xml:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
Adding spring-webmvc to the dependency will automatically include spring-core, spring-beans, and spring-context dependencies. So we can remove them from the pom.xml.

Enabling Web MVC using @EnableWebMvc

To be able to make use of the Spring MVC features, we need to have one class that has been annotated with @Configuration, to be annotated with @EnableWebMvc. The @EnableWebMvc annotation, imports the Spring MVC configuration from the WebMvcConfigurationSupport class present in the Spring MVC framework. If we need to override any of the default imported configuration, we would have to implement the WebMvcConfigurer interface present in the Spring MVC framework and override the required methods.

We will create an AppConfiguration class with the following definition:

@EnableWebMvc
@Configuration
@ComponentScan(basePackages = "com.nilangpatel.worldgdp")
public class AppConfiguration implements WebMvcConfigurer{

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
}

In the previous configuration, a few important things to note are as follows:

  • @EnableWebMvc: This imports the Spring MVC related configuration.
  • @ComponentScan: This is used for declaring the packages that have to be scanned for Spring components (which can be @Configuration, @Service, @Controller, @Component, and so on). If no package is defined, then it scans starting from the package where the class is defined.
  • WebMvcConfigurer: We are going to implement this interface to override some of the default Spring MVC configuration seen in the previous code.

Configuration to deploy to Tomcat without web.xml

As we will be deploying the application to Tomcat, we need to provide the servlet configuration to the application server. We will look at how to deploy to Tomcat in a separate section, but now we will look at the Java configuration, which is sufficient to deploy the application to Tomcat or any application server without the need for an additional web.xml. The Java class definition is given in the following:

public class WorldApplicationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {

@Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] {AppConfiguration.class};
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}

The AbstractAnnotationConfigDispatcherServletInitializer abstract class is an implementation of the WebApplicationInitializer interface that is used to register Spring's DispatcherServlet instance and uses the other @Configuration classes to configure the DispatcherServlet.

We just need to override the getRootConfigClasses(), getServletConfigClasses(), and getServletMappings() methods. The first two methods point to the configuration classes that need to load into the servlet context, and the last method is used to provide the servlet mapping for DispatcherServlet.

DispatcherServlet follows the front controller pattern, where there is a single servlet registered to handle all the web requests. This servlet uses the RequestHandlerMapping and invokes the corresponding implementation based on the URL mapped to the implementation.

We need to make a small update to the Maven WAR plugin so that it doesn't fail if there is no web.xml found. This can be done by updating the <plugins> tag in the pom.xml file, as shown in the following:

<build>
<finalName>worldgdp</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<executions>
<execution>
<id>default-war</id>
<phase>prepare-package</phase>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

Now we are all set to implement our controllers. We will show you how to deploy to Tomcat once we have implemented all the RESTful API controllers.

Defining the RESTful API controller for country resource

Let's define the RESTful API controller for the country resource. The following is the template for the controller:

@RestController
@RequestMapping("/api/countries")
@Slf4j
public class CountryAPIController {

@Autowired CountryDAO countryDao;
@Autowired WorldBankApiClient worldBankApiClient;

@GetMapping
public ResponseEntity<?> getCountries(
@RequestParam(name="search", required = false) String searchTerm,
@RequestParam(name="continent", required = false) String continent,
@RequestParam(name="region", required = false) String region,
@RequestParam(name="pageNo", required = false) Integer pageNo
){
//logic to fetch contries from CountryDAO
return ResponseEntity.ok();
}

@PostMapping(value = "/{countryCode}",
consumes = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<?> editCountry(
@PathVariable String countryCode, @Valid @RequestBody Country country ){
//logic to edit existing country
return ResponseEntity.ok();
}

@GetMapping("/{countryCode}/gdp")
public ResponseEntity<?> getGDP(@PathVariable String countryCode){
//logic to get GDP by using external client
return ResponseEntity.ok();
}

}

The following are a few things to note from the previous code:

  • @RestController: This is used to annotate a class as a controller with each of the RESTful methods returning the data in the response body. 
  • @RequestMapping: This is for assigning the root URL for accessing the resources. 
  • @GetMapping and @PostMapping: These are used to assign the HTTP verbs that will be used to invoke the resources. The URL for the resources are passed within the annotation, along with other request headers that consume and produce information. 

Let's implement each of the methods in order, starting with getCountries(), as shown in the following code:

@GetMapping
public ResponseEntity<?> getCountries(
@RequestParam(name="search", required = false) String searchTerm,
@RequestParam(name="continent", required = false) String continent,
@RequestParam(name="region", required = false) String region,
@RequestParam(name="pageNo", required = false) Integer pageNo
){
try {
Map<String, Object> params = new HashMap<String, Object>();
params.put("search", searchTerm);
params.put("continent", continent);
params.put("region", region);
if ( pageNo != null ) {
params.put("pageNo", pageNo.toString());
}

List<Country> countries = countryDao.getCountries(params);
Map<String, Object> response = new HashMap<String, Object>();
response.put("list", countries);
response.put("count", countryDao.getCountriesCount(params));
return ResponseEntity.ok(response);
}catch(Exception ex) {
log.error("Error while getting countries", ex);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Error while getting countries");
}
}

The following are some of the things to note from the previous code:

  • @RequestParam: This annotation is used to declare request parameters accepted by the controller endpoint. The parameters can be provided with a default value and can also be made mandatory. 
  • ResponseEntity: This class is used to return the response body, along with other response parameters such as status, headers, and so on.

Next up is the API for editing country details, as follows:

@PostMapping("/{countryCode}")
public ResponseEntity<?> editCountry(
@PathVariable String countryCode, @Valid @RequestBody Country country ){
try {
countryDao.editCountryDetail(countryCode, country);
Country countryFromDb = countryDao.getCountryDetail(countryCode);
return ResponseEntity.ok(countryFromDb);
}catch(Exception ex) {
log.error("Error while editing the country: {} with data: {}",
countryCode, country, ex);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Error while editing the country");
}
}

The following are a few things to note from the previous code implementation:

  • @PathVariable: This is used to declare any variable that needs to be part of the URL path of the controller endpoint. In our case, we want the country code to be part of the URL. So the URL will be of the /api/countries/IND form.
  • @Valid: This triggers the Bean Validation API to check for the restrictions on each of the class properties. If the data from the client is not valid, it returns a 400.
  • @RequestBody: This is used to capture the data sent in the request body and the Jackson library is used to convert the JSON data in the request body to the corresponding Java object.

The rest of the API implementation can be found in the CountryAPIController class. The tests for the API controller can be found in the CountryAPIControllerTest class, which is available in the source code of this book.

Defining the RESTful API controller for city resource

For the city resource we would need the following APIs:

  • Get cities for a given country
  • Add a new city to the country
  • Delete the city from the country

The code for this controller can be found in the CityAPIController class and the tests for the API controller can be found in the CityAPIControllerTest class, which is available in the source code of this book.

Defining the RESTful API controller for country language resource

For the CountryLanguage resource we need the following APIs:

  • Get languages for a country
  • Add a language for a country
  • Delete a language from the country

The code for this controller can be found in the CountryLanguageAPIController class and the tests for the API controller can be found in the CountryLanguageAPIControllerTest class, which is available in the source code of this book.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime