Micronautでコントローラクラスを作る方法!@Controllerの基本を初心者向けに徹底解説
生徒
「Micronautでウェブアプリを作りたいんですが、コントローラってどうやって作るんですか?」
先生
「Micronautでは、@Controllerアノテーションをクラスに付けることで、HTTPリクエストを受け取るコントローラクラスを簡単に作ることができます。」
生徒
「アノテーションを付けるだけで動くんですか?Springみたいな感じですか?」
先生
「そうです!Springに似た書き方ですが、Micronautはコンパイル時に処理を行うのでとても高速です。基本から順に見ていきましょう!」
生徒
「GETリクエストやPOSTリクエストはどうやって受け取るんですか?」
先生
「それぞれ@Getや@Postといったアノテーションをメソッドに付けることで対応できます。具体的なコードを見ながら理解していきましょう!」
1. Micronautのコントローラとは何か
Micronautのコントローラとは、クライアント(ブラウザやスマートフォンアプリなど)からのHTTPリクエストを受け取り、適切なレスポンスを返す役割を担うクラスのことです。ウェブアプリケーション開発において、コントローラはリクエストの入り口となる非常に重要な部品です。
たとえば、ユーザーがブラウザで「http://localhost:8080/hello」というURLにアクセスしたとき、そのリクエストを受け取って「こんにちは!」という文字列を返す処理を書く場所がコントローラです。Micronautではこの仕組みをアノテーションベースで非常にシンプルに実装できます。
Micronautはもともとマイクロサービスやサーバーレス環境向けに設計されたフレームワークです。起動速度が非常に速く、メモリ消費が少ないという特徴があります。これはSpring Frameworkのようにリフレクションを多用せず、コンパイル時にDI(依存性の注入)や設定処理を行うという独自のアーキテクチャによるものです。コントローラの仕組みもこの設計思想に基づいています。
2. @Controllerアノテーションの基本的な使い方
Micronautでコントローラクラスを作るには、クラスに@Controllerアノテーションを付けるだけです。このアノテーションはクラスがHTTPリクエストを処理することをMicronautに伝えます。
アノテーションの引数にはベースパスを指定できます。たとえば@Controller("/hello")と書くと、このクラスの全メソッドは「/hello」以下のパスで受け付けるリクエストに対応します。引数を省略した場合はルートパス(「/」)が使われます。
下記は最もシンプルなコントローラの例です。クラスに@Controllerを付け、メソッドに@Getを付けています。
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
@Controller("/hello")
public class HelloController {
@Get
public String index() {
return "こんにちは、Micronautの世界へようこそ!";
}
}
このコードを書いてアプリケーションを起動し、ブラウザで「http://localhost:8080/hello」にアクセスすると、「こんにちは、Micronautの世界へようこそ!」という文字列が返ってきます。たったこれだけの記述でHTTPエンドポイントが完成するのがMicronautの大きな魅力です。
実行結果のイメージは下記のようになります。
こんにちは、Micronautの世界へようこそ!
3. @Getで特定のパスにマッピングする方法
コントローラ内のメソッドにHTTPのGETリクエストを割り当てるには、@Getアノテーションを使います。@Getには引数としてサブパスを指定することができ、@Controllerのベースパスと組み合わせて最終的なURLが決まります。
たとえば@Controller("/users")のクラスの中で@Get("/list")と書いたメソッドは、「/users/list」というパスに対応します。このようにベースパスとサブパスを組み合わせることで、関連するエンドポイントをひとつのコントローラにまとめて管理できます。
下記のサンプルでは、異なるパスに対して複数のGETエンドポイントを定義しています。
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
@Controller("/fruits")
public class FruitController {
@Get("/apple")
public String getApple() {
return "りんごです!";
}
@Get("/banana")
public String getBanana() {
return "バナナです!";
}
@Get("/orange")
public String getOrange() {
return "オレンジです!";
}
}
このコントローラを使うと、「/fruits/apple」「/fruits/banana」「/fruits/orange」という三つのエンドポイントが使えるようになります。それぞれのURLにアクセスすると対応する文字列が返ってきます。
このように、ひとつのコントローラに複数のエンドポイントをまとめて定義できるのがコントローラの便利なところです。関連する処理を同じクラスにまとめることで、コードの見通しがよくなり管理もしやすくなります。
4. パスパラメータを使って動的なURLに対応する方法
ウェブアプリケーションでは「/users/1」「/users/2」のように、URLの一部が動的に変わる場合があります。このような動的なパスの値を受け取るには、パスパラメータという仕組みを使います。
Micronautでは@Getのパスに波括弧で囲んだ名前を書くことでパスパラメータを定義できます。たとえば@Get("/{name}")と書くと、波括弧の部分に入った文字列をメソッドの引数として受け取ることができます。
メソッドの引数には@PathVariableアノテーションを付けるか、パラメータ名をパス定義と同じ名前にすることで自動的にマッピングされます。下記のサンプルで確認してみましょう。
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
@Controller("/greet")
public class GreetController {
@Get("/{name}")
public String greet(String name) {
return "こんにちは、" + name + "さん!Micronautへようこそ!";
}
}
このコントローラを使って「/greet/田中」というURLにアクセスすると、下記のような結果が返ってきます。
こんにちは、田中さん!Micronautへようこそ!
URLの「田中」という部分がメソッドの引数nameに自動的に渡されているのがわかります。パスパラメータを使うことで、ひとつのメソッドで様々なリクエストに対応できるようになります。ユーザー情報の取得や商品詳細ページの表示など、実際のアプリケーション開発で非常によく使われる手法です。
5. @PostでPOSTリクエストを受け取る方法
フォームの送信やデータの登録など、クライアントからサーバーにデータを送る場合はHTTPのPOSTメソッドを使います。MicronautでPOSTリクエストを受け取るには、メソッドに@Postアノテーションを付けます。
POSTリクエストで送られてくるデータ(リクエストボディ)を受け取るには、@Bodyアノテーションを引数に付けます。受け取るデータの形式としてはJSON形式がよく使われます。Micronautは標準でJSONの自動変換をサポートしているので、Javaのクラスを定義しておくだけで自動的にJSONとオブジェクトの相互変換を行ってくれます。
下記のサンプルでは、名前と年齢を受け取るシンプルなPOSTエンドポイントを作っています。
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
@Controller("/register")
public class RegisterController {
@Post
public String register(@Body UserRequest request) {
return request.getName() + "さん(" + request.getAge() + "歳)を登録しました!";
}
// リクエストボディを受け取るための内部クラス
public static class UserRequest {
private String name;
private int age;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
}
このエンドポイントに対して、下記のようなJSON形式でPOSTリクエストを送ると対応した文字列が返ってきます。
{"name": "山田", "age": 25}
レスポンスとして「山田さん(25歳)を登録しました!」という文字列が返ります。@Bodyを使うことでJSONのデータを自動的にJavaオブジェクトに変換してくれるため、開発者はビジネスロジックの記述に集中できます。
6. レスポンスのステータスコードをカスタマイズする方法
HTTPレスポンスにはステータスコードというものがあります。成功した場合は200番台、クライアントのリクエストに問題がある場合は400番台、サーバー側のエラーの場合は500番台のコードが返ります。代表的なものとして、200(OK)、201(Created:リソースの作成成功)、404(Not Found:見つからない)などがあります。
Micronautでは@Statusアノテーションを使ってレスポンスのステータスコードを指定できます。たとえばデータを新しく作成するエンドポイントでは、成功時に「201 Created」を返すのが一般的です。
また、HttpResponseクラスを使うことで、ステータスコードとレスポンスボディをまとめて返すこともできます。より細かい制御が必要な場合に便利です。
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
@Controller("/status")
public class StatusController {
@Get("/ok")
public HttpResponse<String> getOk() {
return HttpResponse.ok("処理が正常に完了しました。");
}
@Get("/created")
public HttpResponse<String> getCreated() {
return HttpResponse.status(HttpStatus.CREATED).body("新しいリソースを作成しました。");
}
@Get("/notfound")
public HttpResponse<String> getNotFound() {
return HttpResponse.notFound("指定したリソースは見つかりませんでした。");
}
}
このようにHttpResponseを使うと、ステータスコードの指定がとても直感的に書けます。HttpResponse.ok()は200、HttpResponse.status(HttpStatus.CREATED)は201、HttpResponse.notFound()は404のステータスコードを持つレスポンスを返します。
ステータスコードを適切に返すことは、APIを利用するクライアント側の開発者にとって非常に重要な情報になります。正しいステータスコードを返す習慣をつけておくと、チーム開発でもトラブルが減ります。
7. クエリパラメータの受け取り方
URLの末尾に「?キー名=値」という形式で付けることができるクエリパラメータは、検索条件や絞り込み条件などを渡すときによく使われます。たとえば「/search?keyword=Micronaut」というURLの「keyword=Micronaut」の部分がクエリパラメータです。
Micronautでクエリパラメータを受け取るには、メソッドの引数に@QueryValueアノテーションを付けます。また、引数をそのままパラメータ名と同じ名前にするだけで自動的にバインドされる場合もあります。
クエリパラメータが任意(省略可能)な場合は、引数の型をOptionalにするか、デフォルト値を設定することで対応できます。下記のサンプルで確認してみましょう。
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.QueryValue;
import java.util.Optional;
@Controller("/search")
public class SearchController {
@Get
public String search(
@QueryValue String keyword,
@QueryValue Optional<Integer> page
) {
int currentPage = page.orElse(1);
return "キーワード「" + keyword + "」で" + currentPage + "ページ目を検索しました!";
}
}
「/search?keyword=Java」というURLでアクセスすると、下記のような結果が返ってきます。
キーワード「Java」で1ページ目を検索しました!
「/search?keyword=Java&page=3」というURLでアクセスすると、下記のような結果が返ります。
キーワード「Java」で3ページ目を検索しました!
Optionalを使うことで、クエリパラメータが省略された場合でもエラーにならずにデフォルト値を使って処理できます。検索機能やページング機能を実装するときに非常に役立つ書き方です。
8. コントローラのベストプラクティスと注意点
Micronautのコントローラを実際のプロジェクトで使うにあたって、いくつか押さえておきたいポイントがあります。初心者のうちから意識しておくと、あとあと保守しやすいコードが書けるようになります。
まず、コントローラには「リクエストの受け取りとレスポンスの返却」という役割だけを持たせるのが理想です。ビジネスロジック(計算処理やデータ加工など)はサービスクラスに切り出し、コントローラからサービスを呼び出す形にすると、コードが読みやすくなりテストもしやすくなります。これは「関心の分離」と呼ばれる設計の考え方で、多くのウェブフレームワークで推奨されています。
次に、エラーハンドリングについても早めに理解しておくとよいです。Micronautには@Errorアノテーションを使ったエラーハンドリングの仕組みがあります。コントローラ内で例外が発生したとき、適切なエラーレスポンスを返せるようにしておくことが重要です。
また、コントローラに付ける@Controllerのパスはわかりやすい名前にすることが大切です。たとえばユーザー管理の処理なら「/users」、商品管理なら「/products」というように、そのコントローラが何を担当するのかがURLを見ただけでわかるようにしておくと、チーム開発や後からコードを読んだときに非常に助かります。
さらに、Micronautはリアクティブプログラミングにも対応しています。処理を非同期で行いたい場合は、メソッドの戻り値をMonoやFlux(RxJavaやProjectReactor)にすることでノンブロッキングな処理が実現できます。最初は同期処理から始めて、慣れてきたら非同期処理にも挑戦してみましょう。
9. コントローラのユニットテストを書く方法
Micronautはテストのしやすさも大きな特徴のひとつです。コントローラのテストを書くことで、実装が正しく動いているかを自動的に確認できるようになります。特にチーム開発や継続的なメンテナンスが必要なプロジェクトでは、テストコードは欠かせません。
Micronautには@MicronautTestアノテーションを使ったテストの仕組みが用意されています。このアノテーションをテストクラスに付けるだけで、Micronautのアプリケーションコンテキストを使ったテストが実行できます。
テストの中ではHttpClientを使って実際にHTTPリクエストを送り、レスポンスの内容やステータスコードを確認することができます。下記は先ほど作成したHelloControllerに対するテストの例です。
import io.micronaut.http.HttpRequest;
import io.micronaut.http.client.HttpClient;
import io.micronaut.http.client.annotation.Client;
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@MicronautTest
public class HelloControllerTest {
@Inject
@Client("/")
HttpClient client;
@Test
public void testHelloEndpoint() {
String result = client.toBlocking()
.retrieve(HttpRequest.GET("/hello"));
assertEquals("こんにちは、Micronautの世界へようこそ!", result);
}
}
このテストでは、/helloに対してGETリクエストを送り、返ってきた文字列が期待通りかどうかを確認しています。テストが通れば実装が正しいことが保証されます。
テストを書く習慣をつけることで、コードの変更時に既存の機能が壊れていないかを素早く確認できるようになります。Micronautはテスト用の機能が充実しているので、ぜひ積極的に活用してみてください。