R e i - D r e a m
for Laravel
Guest
login

最終投稿日:2026年09月20日

【検証環境】- Windows11 - RockyLinux 8 - JDK 17-LTS - Eclipse 2024-09 R - Wildfly 28.0.1

目次

APIの基本(サービス)

pom.xml

API環境を構築するための依存関係を取得します。
pom.xml

<!-- REST API 基本規格 (Jakarta RESTful Web Services 3.1) -->
<dependency>
    <groupId>jakarta.ws.rs</groupId>
    <artifactId>jakarta.ws.rs-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
<!-- JSONバインディング (Javaオブジェクト ⇔ JSON 相互変換) -->
<dependency>
    <groupId>jakarta.json.bind</groupId>
    <artifactId>jakarta.json.bind-api</artifactId>
    <version>3.0.0</version>
    <scope>provided</scope>
</dependency>
<!-- JSON処理 API (JSONの直接操作・パース用) -->
<dependency>
    <groupId>jakarta.json</groupId>
    <artifactId>jakarta.json-api</artifactId>
    <version>2.1.3</version>
    <scope>provided</scope>
</dependency>

ポイント

『JSON』を扱うモジュールは、シェアの面から見れば圧倒的に『Jackson』となります。
ただし『JSON-B』は、Jakarta EEの標準規格となります。
ちょっと悩ましいですね。

両方入れちぇえってのは、ライブラリの衝突など、厄介事に巻き込まれそうなので悪手となります。
因みに『Jackson』モジュールを入れる場合の《pom.xml》は以下となります。
<!-- Jackson Core / Databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.17.2</version> <!-- 2.15以上を推奨 -->
</dependency>
<!-- Jakarta EE (JAX-RS) と Jackson を連携させるプロバイダー -->
<dependency>
    <groupId>com.fasterxml.jackson.jakarta.rs</groupId>
    <artifactId>jackson-jakarta-rs-json-provider</artifactId>
    <version>2.17.2</version>
</dependency>

本サンプルでは『JSON-B』を記載しますが、できる限り『Jackson』での説明もします。

APIサービス作成

サービス側は、他サーバからのAPIリクエストを捌く機能となります。
APIは『エンドポイント』と言われるURLへのリクエストで発火します。
まずは大元のエンドポイントを『jakarta.ws.rs.core.Application』を継承したクラスに設定します。
今回は『API』の『サービス』側のクラスとなりますので、パッケージは『api.service』とします。
それではパッケージに『RestApiService』クラスを作成してみましょう。
src/main/java/api/service/RestApiService.java

package api.service;

import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;

@ApplicationPath("/api")
public class RestApiService extends Application {}

クラスの内容は空で問題ありません。
大事なのは『@ApplicationPath("/api")』で、このアプリのエンドポイントは以下に決定した事です。
    http://localhost:8080/sample-pj/api
実は、エンドポイントはまだ途中です。
次に用途によって変更したいエンドポイントを決定します。
例えば、契約に関するAPIであれば『ContractsApiService』クラスを作成します。
○ GET通信
src/main/java/api/service/ContractsAPIService.java

package api.service;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;

@Path("/contract")
public class ContractsAPIService {
    @GET
    @Path("/keiyaku")
    public String getKeiyakuName(@QueryParam("kei_no") String keiNo) {
        System.out.println("----- getKeiyakuName -----");
        return "00000" + keiNo;
    }
}

このクラスのエンドポイントは『contract』となり、メソッドのエンドポイントは『keiyaku』となります。
つまり、このAPIサービスを利用する為には、以下のURLでアクセスする必要があります。
    http://localhost:8080/sample-pj/api/contract/keiyaku
また『@GET』が付与されているので、当然『GET通信』となります。
試しにビルドして、ターミナルから以下を叩いてみて下さい。
    curl "http://localhost:8080/sample-pj/api/contract/keiyaku?kei_no=abcdefg"
○ POST通信
また、POST送信による通信は以下の様になります。
ContractsAPIService.java

import jakarta.ws.rs.POST;
~ 省略 ~
@Path("/contract")
public class ContractsAPIService {
    @POST
    @Path("/keiyaku")
    public String getKeiyakuName(@FormParam("kei_no") String keiNo) {
        ~ 省略 ~
    }
}

POSTの場合、ターミナルからは以下の様にリクエストします。
    curl -X POST "http://localhost:8080/sample-pj/api/contract/keiyaku" -d "kei_no=abcdefg"
注意

GET通信は問題ないが、POST通信で謎のエラーがでる場合は、もしかして『 不正遷移』設定をしていませんか?

この設定をしていると、リクエスト側に完全ランダムな文字列を要求する事になるため確実に落ちます。
対策は当然『チェックしない』となりますので、Filter処理の直前で以下ソースを挟みトークンチェックをスキップさせます。
// トークンチェックの有無を「appinfo」から確認
private boolean checkAppInfo(String pageName) {
    if (pageName == null || pageName.trim().isEmpty() || !pageName.endsWith(".xhtml")) {
        return false;
    }
    ~ 省略 ~

パラメータにDTOを渡す方法

先ほどの『@QueryParam』『@FormParam』は、パラメータが大量にある場合は非常に面倒です。
そこでお約束の『DTO』にまとめる事でスッキリとしたコードとなります。
任意のパッケージに『KeiyakuApiDto』を作成しましょう。
KeiyakuApiDto

package dto;

import jakarta.json.bind.annotation.JsonbProperty;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;

@Data
public class KeiyakuApiDto {
    @JsonbProperty("kei_no")
    private String keiNo;
    @NotBlank(message = "名前は必須です")
    private String name;
}

『@JsonbProperty("kei_no")』は、マッピング設定となります。
つまりリクエスト側からは『kei_no』をKEYとして送信されるが、JAVA側は『keiNo』に格納しますよって意味です。
『@NotBlank』は、ビーンバリデーションの設定となりますが、単独では発火しません(後述で説明)
jacksonの場合

『@JsonProperty』と記述します。
因みにインポートするクラスは以下となります。
    import com.fasterxml.jackson.annotation.JsonProperty;

次にAPIサービス側のメソッドを修正します。
public String getKeiyakuName(@FormParam("kei_no") String keiNo) {

public String getKeiyakuName(KeiyakuApiDto dto) {
この設定でターミナルから以下を叩いてみましょう
    curl.exe -X POST "http://localhost:8080/sample-pj/api/contract/keiyaku" -H "Content-Type: application/json" -d "{\"kei_no\": \"12345\", \"name\":\"hoge\"}"
また、DTOと言えば『ビーンバリデーション』ですよね。
ビーンバリデーションを利用するには、PIサービス側のメソッドを更に修正します。
public String getKeiyakuName(KeiyakuApiDto dto) {

public String getKeiyakuName(@Valid KeiyakuApiDto dto) {
これで先ほど設定した『@NotBlank』が普通に使えるようになります。
試しに以下を叩いてみましょう。
    curl.exe -X POST "http://localhost:8080/sample-pj/api/contract/keiyaku" -H "Content-Type: application/json" -d "{\"kei_no\": \"12345\"}"
どうでしたか?
以下の様に表示された方はいませんか?
    [] 前は必須です]arg0.name]
はい、文字化けです。
この現象は、RESTEasy標準のデフォルトエラー出力形式である[arg0.name 名前は必須です] が、コンパイルの段階で文字化けを起こして破壊された結果です。
要するにビーンバリデーションの大きな特徴である『処理がプログラマに来る前に』出力されるエラーの事です。
ここの処理で文字化けされると流石に困ります。
ただし解決策はちゃんとありますのでご安心を。
プロバイダクラスを作成し、エラー出力ルールを変更しましょう。
クラス名パッケージ名は自由ですが、以下の様に作成します。
src/main/java/provider/ValidationExceptionMapper.java

package provider;

import jakarta.validation.ConstraintViolationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import java.util.HashMap;
import java.util.Map;

@Provider
public class ValidationExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
    @Override
    public Response toResponse(ConstraintViolationException exception) {
        Map<String, String> errors = new HashMap<>();
        exception.getConstraintViolations().forEach(violation -> {
            // パス(例: "createKeiyaku.dto.name" -> "name")からフィールド名だけを取り出す
            String path = violation.getPropertyPath().toString();
            String fieldName = path.substring(path.lastIndexOf('.') + 1);
            errors.put(fieldName, violation.getMessage());
        });
        // 明示的に UTF-8 指定の JSON としてレスポンスを作成
        return Response.status(Response.Status.BAD_REQUEST)
                .type(MediaType.APPLICATION_JSON + ";charset=UTF-8")
                .entity(errors)
                .build();
    }
}

ここは難しいので『こういう物だ』と割り切りましょう。
ただひとつ安心して欲しいのは『ExceptionMapper』クラスは、Jakarta REST (JAX-RS) という REST API 専用の通信フレームワークの管轄下でのみ動作する仕組みです。
つまり他の処理には影響しません!
さて、これでもう一度先ほどのコマンドでAPIリクエストを送信しましょう。
    {"name":"名前は必須です"}
ちゃんと期待値通りの返却がされたハズです。

JSONを返却する

基本的に現代のモダンなAPIは『JSON』でのやり取りがお約束です。
では返却用のDTOを作成します。
ResponseApiDto

package dto;
import lombok.Data;
@Data
public class ResponseApiDto {
    private int code;
    private String message;
}

では返却用DTOを使ってAPIクラス『ContractsAPIService』を編集します。
ContractsAPIService.java

package api.service;

import dto.KeiyakuApiDto;
import dto.ResponseApiDto;
import jakarta.validation.Valid;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

@Path("/contract")
@Produces(MediaType.APPLICATION_JSON)
public class ContractsAPIService {
    @POST
    @Path("/keiyaku")
    public Response getKeiyakuName(@Valid KeiyakuApiDto dto) {
        System.out.println("----- getKeiyakuName -----");
        ResponseApiDto res = new ResponseApiDto();
        res.setCode(200);
        res.setMessage("正常に処理が完了しました。");
        return Response.status(Response.Status.OK).entity(res).build();
    }
}

さて、以下をリクエストしてみましょう。
    curl.exe -X POST "http://localhost:8080/sample-pj/api/contract/keiyaku" -H "Content-Type: application/json" -d "{\"kei_no\": \"12345\", \"name\":\"hoge\"}"
以下のJSON文字列が返却されます。
    {"code":200,"message":"正常に処理が完了しました。"}
ポイント

見てお分かりの通り『Response.status(Response.Status.OK)』とする事で、Headerには『HTTP/1.1 200 OK』が設定されます。
その他、以下の様にHeaderに値を設定できます。

return Response.status(Response.Status.OK).entity(dto).build();
return Response.status(Response.Status.BAD_REQUEST).entity(dto).build();
return Response.status(Response.Status.NOT_FOUND).entity(dto).build();
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(dto).build();

ログインしてコメントを残そう!!


きっぷる