BogoToBogo
  • Home
  • About
  • Big Data
  • Machine Learning
  • AngularJS
  • Python
  • C++
  • go
  • DevOps
  • Kubernetes
  • Algorithms
  • More...
    • Qt 5
    • Linux
    • FFmpeg
    • Matlab
    • Django 1.8
    • Ruby On Rails
    • HTML5 & CSS

Spring-Boot REST API with CORS App Maven war file deploy to Tomcat - 2020

Duke 512




Bookmark and Share





bogotobogo.com site search:




Note

This post is the extension of the previous Spring-Boot apps. So far, we've been playing with dumb apps, but this time, we'll learn the Spring REST API with Cross-Origin Resource Sharing. Our app will respond with CORS headers.

This tutorial is based on Enabling Cross Origin Requests for a RESTful Web Service.

Primarily we'll run it by creating a standalone application which we package everything in a single, executable JAR file. This is possible thanks to the Spring's support for embedding the Tomcat servlet container as the HTTP runtime.

However, we'll try it more popular way, deploying the app to a Tomcat's external instance (80.1 Create a deployable war file).

In this tutorial, we'll use Maven 3.






Get source code

Though we can start from scratch, we can bypass basic setup steps since we're already familiar with the process of setting basic Spring-boot setup:

$ git clone https://github.com/spring-guides/gs-rest-service-cors.git

We'll add and modify the code during this tutorial.





pom.xml

gs-rest-service-cors/initial/pom.xml:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springframework</groupId>
    <artifactId>gs-rest-service-cors</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.3.RELEASE</version>
    </parent>

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

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


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

</project>

The pom file not only handles dependencies but also tries to find main() method to flag as a runnable class.

Later, we may need to modify the file to make executable war for Tomcat deploy.





Create a resource representation class

We don't have to do anything because the codes are already there. Basically, we deal with three files:

  1. src/main/java/hello/Greeting.java
  2. src/main/java/hello/GreetingController.java
  3. src/main/java/hello/Application.java

Here is the src/main/java/hello/Greeting.java:

package hello;

public class Greeting {

    private final long id;
    private final String content;

    public Greeting(long id, String content) {
        this.id = id;
        this.content = content;
    }

    public long getId() {
        return id;
    }

    public String getContent() {
        return content;
    }
}

At the GET request, the Greeting class responde with JSON in the body that represents a greeting via the Jackson JSON library to automatically marshal instances of type Greeting into JSON.

As we can see soon, we have a RESTful web service controller which populates and returns the Greeting object. The object data will be written directly to the HTTP response as JSON without going through view rendering step.

The greeting object should look like this:

{
    "id": 1,
    "content": "Hello, World!"
}

The id field is a unique identifier for the greeting, and content is the textual representation of the greeting.





Creating a resource controller

src/main/java/hello/GreetingController.java:

package hello;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.CrossOrigin;

@Controller
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @RequestMapping("/greeting")
    public @ResponseBody Greeting greeting(@RequestParam(required=false, defaultValue="World") String name) {
        System.out.println("==== in greeting ====");
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }

}

In Spring, HTTP requests are handled by a controller identified by the @Controller annotation, and the GreetingController above handles GET requests for /greeting by returning a new instance of the Greeting class.

Note that we're using three annotations in the code:

  1. @RequestMapping annotation:

    The /greeting request are mapped to the greeting() method Greeting.java is ensured by the @RequestMapping annotation.

  2. @RequestParam annotation:

    @RequestParam binds the value of the query string parameter name into the name parameter of the greeting() method. If it is not in the request, the defaultValue of "World" is used.
    The greeting() method body creates and returns a new Greeting object with id and content based on the next value from the counter, and formats the given name by using the greeting template.

  3. @@ResponseBody annotation:

    The @@ResponseBody annotation on the greeting() method tells Spring MVC that it does not need to render the greeting object through a server-side view layer. It let's Spring know that the greeting object returned is the response body, and should be written out directly.





Controller method CORS configuration

To controll CORS access, we have to add a @CrossOrigin annotation to the handler method in GreetingController.java:

package hello;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.CrossOrigin;

@Controller
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @CrossOrigin(origins = "http://localhost:9000")
    @RequestMapping("/greeting")
    public @ResponseBody Greeting greeting(@RequestParam(required=false, defaultValue="World") String name) {
        System.out.println("==== in greeting ====");
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }

}

By default, the @CrossOrigin annotation allows all origins, all headers, but in our case, we enabled cross-origin requests only for headers.





Make the application executable

We'll package everything in a single, executable JAR file by embedding the Tomcat servlet container as the HTTP runtime.

Here is src/main/java/hello/Application.java file:

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

The main() method uses Spring Boot's SpringApplication.run() method to launch our application.

We can run the app using mvn spring-boot:run:

$ mvn spring-boot:run

RESTAPI-ID-1.png
RESTAPI-ID-2.png

Notice that the id attribute has changed from 1 to 2 when we make another request. This proves that we are working against the same GreetingController instance across multiple requests, and that its counter field is being incremented on each call as expected.

If we add a query string, we get the following:

RESTAPI-ID-3.png



Testing CORS

To test that the CORS headers are in place and allowing a Javascript client from another origin to access the service, we need to create a Javascript client to consume the service.

Since we cloned the repo, it's already there, gs-rest-service-cors/complete/public/hello.js:

$(document).ready(function() {
    $.ajax({
        url: "http://localhost:8080/greeting"
    }).then(function(data, status, jqxhr) {
       $('.greeting-id').append(data.id);
       $('.greeting-content').append(data.content);
       console.log(jqxhr);
    });
});

This Javascript uses jQuery to consume the REST service at http://localhost:8080/greeting. It is loaded by index.html as shown below:

<!DOCTYPE html>
<html>
    <head>
        <title>Hello CORS</title>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
        <script src="hello.js"></script>
    </head>

    <body>
        <div>
            <p class="greeting-id">The ID is </p>
            <p class="greeting-content">The content is </p>
        </div>
    </body>
</html>

Since we're already using the port 8080, we need to be sure to start the client from another server and/or port. This will not only avoid a collision between the two applications, but will also ensure that the client code is served from a different origin than the service. To start the client running on localhost, port 9000:

$ mvn spring-boot:run -Dserver.port=9000

Once the client starts, open http://localhost:9000 in a browser, where we should see:

RESTAPI-port9000.png



Deploying to Tomcat's external instance

Let's deploy the app to a Tomcat's external instance. To do that we need to modify our pom.xml:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springframework</groupId>
    <artifactId>gs-rest-service-cors</artifactId>
    <version>0.1.0</version>
    <packaging>war</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.3.RELEASE</version>
    </parent>

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

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

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

</project>

Two things:

  1. Note that we used packaging element to specify war file we want to create.
  2. To build a war file that is both executable and deployable into an external container we need to mark the embedded container dependencies as provided to avoid embedding the Tomcat servlet container as the HTTP runtime.

The last step for deplying to Tomcat is to modify our main():

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.boot.context.web.SpringBootServletInitializer;


@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }

}

Here we provide a SpringBootServletInitializer subclass and override its configure method. This makes use of Spring Framework's Servlet 3.0 support and allows us to configure our application when it's launched by the servlet container. Typically, we update our application's main class to extend SpringBootServletInitializer as shown above.


Now everything is ready, let's create our war. We can do using one of the following commands:

$ mvn clean install

Or:

$ mvn package

Wake up our Tomcat:

$ sudo service tomcat start

Deploy it via "Tomcat Web Application Manager":

WAR-file-to-deploy.png

Here is the Tomcat deployed app:

Deployed0-to-Tomcat.png



Multiple HTTP requests

With the current controller, we cannot server the request "/". So, we may just want to add it to our existing controller, src/main/java/hello/GreetingController.java:

@RequestMapping({"/greeting", "/"})

As we can see, multiple requests using different HTTP verbs can be mapped to the same controller method.

Here is the response to "localhost:8080/?name=root:

RESTAPI-Root.png







Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization

YouTubeMy YouTube channel

Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong







Spring Boot



Spring Boot : Hello world with Mavan 3

Spring Boot : Hello world with Gradle 2

Spring Boot (Gradle 2) : Hello world with Authentication

Spring Boot : Deploying War file to Tomcat 8's webapps

How to Setup Apache as Reverse Proxy for Tomcat Server using mod proxy

Maven : mvn command cheat sheet

Spring-Boot REST API with CORS App Maven war file deploy to Tomcat

Spring-Boot / Spring Security with AngularJS - Part I (Introduction)

Spring-Boot / Spring Security with AngularJS - Part II (Dynamic resource load from Angular)

Spring-Boot / Spring Security with AngularJS : Part III (Form-based Authentication)




Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong







Java Tutorials



Java Tutorial Home

Basics - Compiling and Launching

Inner Classes

Constructor

Enums

Static & Finally

Default and Protected

Polymorphism

Exception Handling

Exception Handling II

String Class

Threads

Threads II - State Transition

Threads III - Synchronization

Object Class

File I/O

Serialization

ArrayList

Autoboxing

Java Graphics Interface I - Basics

Java Graphics Interface II - Labels, Text Fields, Layouts

Java Graphics Interface III - paintComponent

TCP Sockets Server/Client

Scala - Functional Java Programming

Apache CXF install

Tomcat 7 Ubuntu 14 Install on Amazon EC2 instance

What is Apache Maven?

Maven life cycle

Eclipse Maven 3 plugin on Ubuntu 14.04

Apache Maven 3 - Setting up and creating a project

Apache Maven 3 - Compile, build, and install a Maven project

Apache Maven 3 - Dependencies

Apache Maven 3 - Web Application

Apache Maven 3 - Plugins (compiler)

Apache Maven 3 - Plugins (Jetty)

Eclipse CDT / JNI (Java Native Interface) / MinGW



Spring Framework

Hello World App with Spring 4 & Maven 3 - Part I





JUnit & Maven Tutorial



JUnit 4 Introduction (Hello World)

JUnit 4 Test with Eclipse Luna (Hello World)

JUnit 4 Test with Maven (Hello World)











Contact

BogoToBogo
contactus@bogotobogo.com

Follow Bogotobogo

About Us

contactus@bogotobogo.com

YouTubeMy YouTube channel
Pacific Ave, San Francisco, CA 94115

Pacific Ave, San Francisco, CA 94115

Copyright © 2024, bogotobogo
Design: Web Master