Try Spring Boot
Tags: java, springboot
Published: 2023-11-12 17:01:06

## Intro
* [[VSCode] Try Java](https://dev.to/masanori_msl/vscode-try-java-12dl)

In this time, I will create a web application with Spring Boot.

## Adding a Thymeleaf page
First, I will add pages to access web APIs.

To use Thymeleaf, I add "org.springframework.boot:spring-boot-starter-thymeleaf" into build.gradle.

#### build.gradle
```groovy
plugins {
	id 'java'
	id 'org.springframework.boot' version '3.1.5'
	id 'io.spring.dependency-management' version '1.1.3'
}
group = 'jp.masanori'
version = '0.0.1-SNAPSHOT'
java {
	sourceCompatibility = '17'
}
repositories {
	mavenCentral()
}
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
	useJUnitPlatform()
}
```

I can put an HTML file as Thymeleaf template file into src/main/resources/templates.

#### index.html
```html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>Hello</title>
        <meta charset="utf-8">
    </head>
    <body>
        <h1>Hello world!</h1>
        <p class="template_value" th:text="|Hello ${name}!!!|" />
        <button onclick="IndexPage.getKey()">Get key</button>
        <script src="/js/index.page.js"></script>
        <script>IndexPage.init("Hello world!");</script>
    </body>
</html>
```

The controller class should has a "@Controller" anotation.

#### PageController.java
```java
package jp.masanori.springbootsample;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class PageController {
    
    @GetMapping("/")
    public String hello(@RequestParam(value = "name", defaultValue = "World") String name, Model model) {
      model.addAttribute("name", name);
      return "index";
    }
}
```

I put JavaScript files and CSS files into src/main/resources/static as static files.

* [Getting Started|Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)

## CORS
I will try getting string data from another Spring Boot application(localhost:8081).

#### [localhost:8080] index.page.ts
```ts
window.IndexPage = {
    init(message: string) {
        console.log(message);

    },
    getKey() {
        fetch("http://localhost:8081/key", {
            mode: "cors",
            "method": "GET"
        }).then(res => res.text())
        .then(res => console.log(res))
        .catch(err => console.error(err));
    }
}
```

#### [localhost:8081] ApiController.java
```java
package jp.masanori.corssample.controllers;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ApiController {
    private static final String ALGORITHM = "HmacSHA256";

    @GetMapping("/key")
    public String getKey() {
        String key = generateKey();
        return key;
    }

    private String generateKey() {
        try {
            DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS");
            String currentTime = LocalDateTime.now().format(dateFormat);
            Mac sha256 = Mac.getInstance(ALGORITHM);

            sha256.init(new SecretKeySpec(currentTime.getBytes("UTF-8"), ALGORITHM));
            var base64 = Base64.getEncoder();
            return base64.encodeToString(sha256.doFinal(currentTime.getBytes("UTF-8")));
        } catch (NoSuchAlgorithmException ex) {
            System.out.println(ex.getMessage());
        } catch (InvalidKeyException ex) {
            System.out.println(ex.getMessage());
        } catch (UnsupportedEncodingException ex) {
            System.out.println(ex.getMessage());
        }
        return "";
    }
}
```

But my request is blocked by CORS error.


```
Access to fetch at 'http://localhost:8081/key' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
```

To resolve the error, I add configurations of CORS into the Spring Boot projects.

#### [localhost:8081] CorsConfigurer.java
```java
package jp.masanori.corssample.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfigurer implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/*")
                .allowedOrigins("http://localhost:8080", "http://localhost:8081")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTION")
                .allowedHeaders("*")
                // To set custom headers
                .exposedHeaders("Sample-Key")
                .allowCredentials(false).maxAge(3600);
    }
}
```

* [CORS support in Spring Framework](https://spring.io/blog/2015/06/08/cors-support-in-spring-framework)
* [Getting Started|Enabling Cross Origin Requests for a RESTful Web Service](https://spring.io/guides/gs/rest-service-cors/)

## Getting parameters
I can get data what are sent from clients by annotations.

### URL params
#### [localhost:8081] ApiController.java
```java
...
    @GetMapping("/params")
    public String getUrlParams(@RequestParam(value = "name") String name,
            @RequestParam(value = "message") String message) {
        return String.format("Name: %s Message: %s", name, message);
    }
...
```

#### Request URL
```
http://localhost:8081/params?name=masa&message=hello+world
```

### Request Body
#### [localhost:8080] index.page.ts
```ts
...
    sendMessage() {
        const message = {
            name: "masa",
            message: "hello world"
        };
        fetch("http://localhost:8081/params", {
            mode: "cors",
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify(message),
        }).then(res => res.text())
            .then(res => {
                console.log(res);

            }).catch(err => console.error(err));
    }
}
```

#### [localhost:8081] ApiController.java
```java
...
    @PostMapping("/params")
    public String getBodyParams(@RequestBody SampleMessage message) {
        return String.format("Name: %s Message: %s", message.getName(), message.getMessage());
    }
...
```

* [@RequestBody::Spring Framework](https://docs.spring.io/spring-framework/reference/web/webflux/controller/ann-methods/requestbody.html)