DEV Community

Masui Masanori
Masui Masanori

Posted on

1 1 1 1 1

Try Micronaut

Intro

In this time, I will try Micronaut.
I will add a page and uploading files function.

Installation and creating a application

To create a Micronaut application, I should install CLI tool.

After installing, I create a simple application.

mn create-app micronaut-sample
Enter fullscreen mode Exit fullscreen mode

Adding a Thymeleaf page

Because the created project only has a main class and some property files, I will add a controller class and a Thymeleaf page.

build.gradle.kts

plugins {
    id("com.github.johnrengelman.shadow") version "8.1.1"
    id("io.micronaut.application") version "4.2.1"
    id("io.micronaut.aot") version "4.2.1"
}
version = "0.1"
group = "micronaut.sample"
repositories {
    mavenCentral()
}
dependencies {
    annotationProcessor("io.micronaut:micronaut-http-validation")
    annotationProcessor("io.micronaut.serde:micronaut-serde-processor")
    implementation("io.micronaut.serde:micronaut-serde-jackson")

    // Add this
    implementation("io.micronaut.views:micronaut-views-thymeleaf")

    compileOnly("io.micronaut:micronaut-http-client")
    runtimeOnly("ch.qos.logback:logback-classic")
    testImplementation("io.micronaut:micronaut-http-client")
}
application {
    mainClass.set("micronaut.sample.Application")
}
java {
    sourceCompatibility = JavaVersion.toVersion("17")
    targetCompatibility = JavaVersion.toVersion("17")
}
graalvmNative.toolchainDetection.set(false)
micronaut {
    runtime("netty")
    testRuntime("junit5")
    processing {
        incremental(true)
        annotations("micronaut.sample.*")
    }
    aot {
        optimizeServiceLoading.set(false)
        convertYamlToJava.set(false)
        precomputeOperations.set(true)
        cacheEnvironment.set(true)
        optimizeClassLoading.set(true)
        deduceEnvironment.set(true)
        optimizeNetty.set(true)
    }
}
Enter fullscreen mode Exit fullscreen mode

src/main/java/micronaut/sample/pages/PageController.java

package micronaut.sample.pages;

import io.micronaut.http.HttpResponse;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Produces;
import io.micronaut.scheduling.TaskExecutors;
import io.micronaut.scheduling.annotation.ExecuteOn;
import io.micronaut.views.View;

@Controller("/")
public class PageController {
    @Produces(MediaType.TEXT_HTML)
    @ExecuteOn(TaskExecutors.BLOCKING) 
    @View("uploadFiles")
    @Get("/files")
    public HttpResponse<String> getFilePage() {
        return HttpResponse.ok();
    }
}
Enter fullscreen mode Exit fullscreen mode

src/main/resources/views/uploadFiles.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Uploading File Page</title>
        <meta charset="utf-8">
    </head>
    <body>
        <h1>Hello World!</h1>
        <div>
            <input type="file" id="speadsheet_input">
            <button onclick="UploadFilePage.send()">Send</button>
            <script src="js/uploadFiles.page.js"></script>
        </div>
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode

ts/uploadFiles.page.ts

window.UploadFilePage = {
    send() {
        const fileInput = document.getElementById("speadsheet_input") as HTMLInputElement;
        const files = fileInput.files;
        if(files == null || files.length <= 0) {
            return;
        }        
        if(files[0] == null) {
            return;
        }
        const form = new FormData();
        form.append("file", files[0]);
        fetch("http://localhost:8080/files/spreadsheets", {
            method: "POST",
            mode: "cors",
            headers: {
                "fileName": files[0].name
            },
            body: form
        }).then((res) => res.text())
        .then((res) => console.log(res))
        .catch(err => console.error(err));
    },
}
Enter fullscreen mode Exit fullscreen mode

Adding static files

To load JavaScript(TypeScript) files and CSS files, I will add a path to publish static files.

src/main/resources/application.properties

micronaut.application.name=micronaut-sample

# Add this
micronaut.router.static-resources.*.enabled=true
micronaut.router.static-resources.*.paths=classpath:static
Enter fullscreen mode Exit fullscreen mode

After adding these two lines, I can access the files in "src/main/resources/static".

Receiving and reading files

I can receive "multipart/form-data" as "io.micronaut.http.server.multipart.MultipartBody".

src/main/java/micronaut/sample/files/FileController.java

package micronaut.sample.files;

import java.io.IOException;
import java.util.Optional;

import io.micronaut.http.HttpHeaders;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.server.multipart.MultipartBody;

@Controller("/files")
public class FileController {
    @Post(uri = "/spreadsheets", consumes = MediaType.MULTIPART_FORM_DATA, produces = MediaType.TEXT_PLAIN)
    public String uploadSpreadsheet(HttpHeaders headers, @Body MultipartBody file) {
        return "Not implemented";
    }
}
Enter fullscreen mode Exit fullscreen mode

Reading received files

"MultipartBody" extends "Publisher< CompletedPart>".
I will add Project Reactor to read received files.

build.gradle.kts

...
dependencies {
    annotationProcessor("io.micronaut:micronaut-http-validation")
    annotationProcessor("io.micronaut.serde:micronaut-serde-processor")
    implementation("io.micronaut.serde:micronaut-serde-jackson")
    implementation("io.micronaut.views:micronaut-views-thymeleaf")

    // add this
    implementation("io.projectreactor:reactor-core:3.6.1")

    compileOnly("io.micronaut:micronaut-http-client")
    runtimeOnly("ch.qos.logback:logback-classic")
    testImplementation("io.micronaut:micronaut-http-client")
}
...
Enter fullscreen mode Exit fullscreen mode

FileController.java

package micronaut.sample.files;

import java.io.IOException;
import java.util.Optional;

import io.micronaut.http.HttpHeaders;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.server.multipart.MultipartBody;
import reactor.core.publisher.Mono;

@Controller("/files")
public class FileController {
    @Post(uri = "/spreadsheets", consumes = MediaType.MULTIPART_FORM_DATA, produces = MediaType.TEXT_PLAIN)
    public Mono<String> uploadSpreadsheet(HttpHeaders headers, @Body MultipartBody file) {
        return Mono.from(file).map(f -> {
                try {
                    byte[] fileData = f.getBytes();
                    Optional<String> fn = headers.findFirst("fileName");
                    String fileName = "";
                    if(fn.isPresent()) {
                        fileName = fn.get();
                    }
                    return "File: " + fileName + " Len: " + fileData.length;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return "";
            });
    }
}
Enter fullscreen mode Exit fullscreen mode

Receiving over 1MB files

By default, the application only can receive less than 1MB files.
To change the limitation, I add "micronaut.server.multipart.max-file-size" into application.properties.

src/main/resources/application.properties

micronaut.application.name=micronaut-sample
micronaut.router.static-resources.*.enabled=true
micronaut.router.static-resources.*.paths=classpath:static

# add this
micronaut.server.multipart.max-file-size=20971520
Enter fullscreen mode Exit fullscreen mode

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (3)

Collapse
 
emmysteven profile image
Emmy Steven

Can you point me to a resource for unit testing for Quarkus? I'll be more than grateful if you do.

Collapse
 
masanori_msl profile image
Masui Masanori

Thank you for reading my post.
I haven't tried Quarkus yet. If I come into contact with Quarkus in the future, I will post about it.

Collapse
 
insouciantqualms profile image
InsouciantQualms

Using it myself and loving the speed, lightweight footprint and easy of integrating with GraalVM, AWS Lambda and gRPC.

Heroku

Simplify your DevOps and maximize your time.

Since 2007, Heroku has been the go-to platform for developers as it monitors uptime, performance, and infrastructure concerns, allowing you to focus on writing code.

Learn More