Pages

Monday, November 22, 2021

Building First Spring Boot REST API Application (Hello World Example)

1. Overview


In this tutorial, You'll learn how to create the first spring boot application in Spring Boot. This is like spring boot Hello world example. You will learn today how to create a spring boot rest API application from scratch step by step.

Building First Spring Boot REST API Application (Hello World Example)


Spring Boot is a subproject initiative done by the spring core development team. It manages internally many things for us automatically such as managing dependencies, spring traditional configurations, deploying in embed tomcat server and hosting as rest API.

Spring Boot 2.2.1
Java 1.8



2. Creating a Spring Boot Project Skeleton


Spring Team provided a simpler way to create a basic spring boot project. To create, Open a website "https://start.spring.io/" and it provides the below options. It supports maven and Gradle build tools, as well as spring boot application, which can be developed in Java, kotlin and groovy languages.


1. start.spring.io


Select the stable spring boot version and now selecting 2.2.1 version in this example. Use the keyboard keywords to explore the project to be created "CTRL + Space". Then a new window will be opened and shows the boot project structure.



2.explore-spring-boot-project


Verify once all configurations are good in pom.xml file then click on "Download as ZIP". Spring boot sample project will be downloaded which is ready to deploy into production with minimal changes. And also this project runs instantly without any changes.

3. Spring Boot basic pom.xml


Below is a pom.xml file generated using a string.start.io smart app.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.2.1.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
 </parent>
 <groupId>first.application</groupId>
 <artifactId>spring-boot</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <name>spring-boot</name>
 <description>Demo project for Spring Boot</description>

 <properties>
  <java.version>1.8</java.version>
 </properties>

 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
   <exclusions>
    <exclusion>
     <groupId>org.junit.vintage</groupId>
     <artifactId>junit-vintage-engine</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
 </dependencies>

 <build>
  <plugins>
   <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
   </plugin>
  </plugins>
 </build>

</project>


The main dependency is "spring-boot-starter-web" which bundles all dependent jars in once place and avoids the version mismatches.

4. Show Spring Boot Dependency Structure:


See the dependencies tree structure.

mvn dependency:tree


$ mvn dependency:tree
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building spring-boot 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:3.1.1:tree (default-cli) @ spring-boot ---
[INFO] first.application:spring-boot:jar:0.0.1-SNAPSHOT
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:2.2.1.RELEASE:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter:jar:2.2.1.RELEASE:compile
[INFO] |  |  +- org.springframework.boot:spring-boot:jar:2.2.1.RELEASE:compile
[INFO] |  |  +- org.springframework.boot:spring-boot-autoconfigure:jar:2.2.1.RELEASE:compile
[INFO] |  |  +- org.springframework.boot:spring-boot-starter-logging:jar:2.2.1.RELEASE:compile
[INFO] |  |  |  +- ch.qos.logback:logback-classic:jar:1.2.3:compile
[INFO] |  |  |  |  \- ch.qos.logback:logback-core:jar:1.2.3:compile
[INFO] |  |  |  +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.12.1:compile
[INFO] |  |  |  |  \- org.apache.logging.log4j:log4j-api:jar:2.12.1:compile
[INFO] |  |  |  \- org.slf4j:jul-to-slf4j:jar:1.7.29:compile
[INFO] |  |  +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
[INFO] |  |  \- org.yaml:snakeyaml:jar:1.25:runtime
[INFO] |  +- org.springframework.boot:spring-boot-starter-json:jar:2.2.1.RELEASE:compile
[INFO] |  |  +- com.fasterxml.jackson.core:jackson-databind:jar:2.10.0:compile
[INFO] |  |  |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.10.0:compile
[INFO] |  |  |  \- com.fasterxml.jackson.core:jackson-core:jar:2.10.0:compile
[INFO] |  |  +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.10.0:compile
[INFO] |  |  +- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.10.0:compile
[INFO] |  |  \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.10.0:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter-tomcat:jar:2.2.1.RELEASE:compile
[INFO] |  |  +- org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.27:compile
[INFO] |  |  +- org.apache.tomcat.embed:tomcat-embed-el:jar:9.0.27:compile
[INFO] |  |  \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:9.0.27:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter-validation:jar:2.2.1.RELEASE:compile
[INFO] |  |  +- jakarta.validation:jakarta.validation-api:jar:2.0.1:compile
[INFO] |  |  \- org.hibernate.validator:hibernate-validator:jar:6.0.18.Final:compile
[INFO] |  |     +- org.jboss.logging:jboss-logging:jar:3.4.1.Final:compile
[INFO] |  |     \- com.fasterxml:classmate:jar:1.5.1:compile
[INFO] |  +- org.springframework:spring-web:jar:5.2.1.RELEASE:compile
[INFO] |  |  \- org.springframework:spring-beans:jar:5.2.1.RELEASE:compile
[INFO] |  \- org.springframework:spring-webmvc:jar:5.2.1.RELEASE:compile
[INFO] |     +- org.springframework:spring-aop:jar:5.2.1.RELEASE:compile
[INFO] |     +- org.springframework:spring-context:jar:5.2.1.RELEASE:compile
[INFO] |     \- org.springframework:spring-expression:jar:5.2.1.RELEASE:compile
[INFO] \- org.springframework.boot:spring-boot-starter-test:jar:2.2.1.RELEASE:test
[INFO]    +- org.springframework.boot:spring-boot-test:jar:2.2.1.RELEASE:test
[INFO]    +- org.springframework.boot:spring-boot-test-autoconfigure:jar:2.2.1.RELEASE:test
[INFO]    +- com.jayway.jsonpath:json-path:jar:2.4.0:test
[INFO]    |  +- net.minidev:json-smart:jar:2.3:test
[INFO]    |  |  \- net.minidev:accessors-smart:jar:1.2:test
[INFO]    |  |     \- org.ow2.asm:asm:jar:5.0.4:test
[INFO]    |  \- org.slf4j:slf4j-api:jar:1.7.29:compile
[INFO]    +- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.2:test
[INFO]    |  \- jakarta.activation:jakarta.activation-api:jar:1.2.1:test
[INFO]    +- org.junit.jupiter:junit-jupiter:jar:5.5.2:test
[INFO]    |  +- org.junit.jupiter:junit-jupiter-api:jar:5.5.2:test
[INFO]    |  |  +- org.apiguardian:apiguardian-api:jar:1.1.0:test
[INFO]    |  |  +- org.opentest4j:opentest4j:jar:1.2.0:test
[INFO]    |  |  \- org.junit.platform:junit-platform-commons:jar:1.5.2:test
[INFO]    |  +- org.junit.jupiter:junit-jupiter-params:jar:5.5.2:test
[INFO]    |  \- org.junit.jupiter:junit-jupiter-engine:jar:5.5.2:test
[INFO]    |     \- org.junit.platform:junit-platform-engine:jar:1.5.2:test
[INFO]    +- org.mockito:mockito-junit-jupiter:jar:3.1.0:test
[INFO]    +- org.assertj:assertj-core:jar:3.13.2:test
[INFO]    +- org.hamcrest:hamcrest:jar:2.1:test
[INFO]    +- org.mockito:mockito-core:jar:3.1.0:test
[INFO]    |  +- net.bytebuddy:byte-buddy:jar:1.10.2:test
[INFO]    |  +- net.bytebuddy:byte-buddy-agent:jar:1.10.2:test
[INFO]    |  \- org.objenesis:objenesis:jar:2.6:test
[INFO]    +- org.skyscreamer:jsonassert:jar:1.5.0:test
[INFO]    |  \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO]    +- org.springframework:spring-core:jar:5.2.1.RELEASE:compile
[INFO]    |  \- org.springframework:spring-jcl:jar:5.2.1.RELEASE:compile
[INFO]    +- org.springframework:spring-test:jar:5.2.1.RELEASE:test
[INFO]    \- org.xmlunit:xmlunit-core:jar:2.6.3:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.489 s
[INFO] Finished at: 2019-11-08T14:49:24+05:30
[INFO] Final Memory: 26M/309M


Take a look at the dependency tree. It is getting Jackson API, embed tomcat API, all spring jars, JUnit, mokito and all others.

5. Creating REST Controller


We are now creating a rest api that has two requests. Those are one post and one gets requests.

The scenario is our rest endpoint should take the orders and display them to the client. All orders will be stored in memory and here storing them in List.

First Creating model class Order.java

Order.java

package model;

import com.fasterxml.jackson.annotation.JsonProperty;

public class Order {

 @JsonProperty("id")
 private int id;

 @JsonProperty("product name")
 private String productName;

 @JsonProperty("quantity")
 private int quantity;

 public int getId() {
  return id;
 }

 public void setId(int id) {
  this.id = id;
 }

 public String getProductName() {
  return productName;
 }

 public void setProductName(String productName) {
  this.productName = productName;
 }

 public int getQuantity() {
  return quantity;
 }

 public void setQuantity(int quantity) {
  this.quantity = quantity;
 }
}

Creating a RestController:


To create a rest API endpoint, first, we need to create a controller class with @RestController annotation. This annotation tells the spring boot that its controller provides some rest api methods. After that should add @RequestMapping() annotation with URI as "api/v1/order".

Once the application is started, we can access the service via endpoint localhost:8080/api/v1/order.

package api;

import model.Order;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;


@RestController
@RequestMapping("api/v1/order")
public class OrdersController {

    private List orders = new ArrayList<>();

    @PostMapping
    public void sendOrder(@RequestBody Order order){
        System.out.println("inside post method.");
        orders.add(order);
    }

    @GetMapping
    public List getOrders(){
        return orders;
    }

}

sendOrder(Order order) method handles the post requests and it is mapped using @PostMapping annotation. getOrder() method is mapped to the get requests using @GetMapping.

We are going to send the order as JSON to the post method so we need to declare using @RequestBody annotation to tell that Order details will be sent as part of the request message body.

Starting SpringBoot Application:


Now, everything is set good to start our first spring boot application. Just run the Application.java class from eclipse or your IDE as a java application. Then the tomcat embed server will be launched and the console pops up with the log information about the application.


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.1.RELEASE)

2019-11-08 17:59:13.908  INFO 22292 --- [           main] f.application.springboot.Application     : Starting Application on L-MAA-25015149 with PID 22292 (C:\Users\vnukala\Desktop\1\spring-boot\target\classes started by vnukala in C:\Users\vnukala\Desktop\1\spring-boot)
2019-11-08 17:59:13.918  INFO 22292 --- [           main] f.application.springboot.Application     : No active profile set, falling back to default profiles: default
2019-11-08 17:59:15.408  INFO 22292 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-11-08 17:59:15.419  INFO 22292 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-11-08 17:59:15.419  INFO 22292 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.27]
2019-11-08 17:59:15.543  INFO 22292 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-11-08 17:59:15.545  INFO 22292 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1535 ms
2019-11-08 17:59:15.793  INFO 22292 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-11-08 17:59:16.075  INFO 22292 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-11-08 17:59:16.079  INFO 22292 --- [           main] f.application.springboot.Application     : Started Application in 2.716 seconds (JVM running for 3.204)
2019-11-08 18:01:14.940  INFO 22292 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-11-08 18:01:14.940  INFO 22292 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2019-11-08 18:01:14.947  INFO 22292 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 6 ms
inside post method.

6. How to Test the Spring Boot Rest API:


Testing can be done from any browser such as chrome, Mozilla or any by just hitting the endpoint. But, here you need to post the order information as JSON to post method. In such cases, it is a bit tough to handle from browsers. So we are going to show to a toll PostMan which is well developed and used by top IT companies. This is a free tool and download link is here.


Enter the URL in field "Enter request URL" as "localhost:8080/api/v1/order". Next, select POST from the dropdown as below.


post-method-selection


Click on Body tab and select "raw" radio button then select "JSON(application/json)" from right side dropdown. Hit the "Send" button. It will send the request and displays the response as "200" if it is a success.



spring-boot-rest-api-test-post-request


Send another post request with the below json order details.
{
 "id": "2",
 "product name" : "iphone 10 plus",
 "quantity": "2"
}

Now select reqeust type as "GET" from the dropdown and hit the send button. Now it should retrieve all orders from memory and display them.

spring-boot-rest-api-test-get-request


This worked as expected. You can try now from your side. Before that take a look at the following errors that faced during application development and testing it.

7. Problems occurred during the starting of this spring boot application:


Showing now all the issues encountered in building our 1st boot application.

This application is executed by a new developer to the spring boot. So whatever issues faced, all are listing here. Please read these issues, these would be very helpful in resolving in real-time.

Spring boot application not starting embedded tomcat?


If normal spring dependency added in pom.xml "spring-boot-starter" then no tomcat will be started. To solve this issue, please add "spring-boot-starter-web".

2019-11-08 14:53:05.143  INFO 7892 --- [           main] f.application.springboot.Application     : Starting Application on L-MAA-25015149 with PID 7892 (C:\Users\vnukala\Desktop\1\spring-boot\target\classes started by vnukala in C:\Users\vnukala\Desktop\1\spring-boot)
2019-11-08 14:53:05.149  INFO 7892 --- [           main] f.application.springboot.Application     : No active profile set, falling back to default profiles: default
2019-11-08 14:53:06.194  INFO 7892 --- [           main] f.application.springboot.Application     : Started Application in 1.815 seconds (JVM running for 2.475)

Process finished with exit code 0

After adding the spring web, the spring boot application starts normally and shows a tomcat running port on the console.

[           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''

Endpoint URI is not working (Not able to hit controller)?


I have configured the rest controller with @RestController annotation and added mapping URI as well. But when tried from the postman, it always resulted in a 404 error code.

Post method End point: localhost:8080/api/v1/order

Error message:

{
    "timestamp": "2019-11-08T11:33:24.453+0000",
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/api/v1/order"
}


spring-boot-404-error-code


I have analyzed the issue many times but could not figure it ours from the console. The reason behind this is no error message was shown in the console as nothing is printed. So, Finally guessed that the executor is not coming to the controller post method. To confirm this added System.out.println("post method.").

Controller class "OrdersController.java" is in the api package and "Application.java" to start the spring boot application is in a different package.
If both are in the same package then the post method will be executed with no errors.

The solution is to tell the spring boot scan my controller package so that my post and get rest api methods will be executed. Just add @ComponentScan annotation along with @SpringBootApplication as below.

@ComponentScan("api")
@SpringBootApplication

JSON property mismatch or property ignorance:


Some of the cases, JSON provided in post request will not be parsed by Jackson API. If there is any mismatch with the model property name with JSON property in the post request then default value null is assigned.

{
 "id": "1",
 "name" : "iphone 10",
 "quantity": "1"
}

Here "name" is given but in the Model Order.java, it is mentioned as "productName" variable name. No annotation is used.

To solve this we should tell Jackson API use "product name" for productName property using @JSONProperty annotation.

 @JsonProperty("product name")
 private String productName;


8. Conclusion


In this article, we have seen how to build the first spring boot rest API example with step by step approach. And also shown the problems faced during the development of this basic rest api and how to troubleshoot easily.

You can read about Spring Security Tutorial on how to apply robust authentication and authorization techniques from Spring framework.

No comments:

Post a Comment

Please do not add any spam links in the comments section.