3. レスポンス形式とエラー処理
V5 API のすべての呼び出しは 型付き DTO または boolean/long を返します。失敗時は以下の安全なデフォルト値を返します。
3.1 V5 レスポンスパターン
| メソッドパターン | 成功時の戻り値 | 失敗時の戻り値 |
|---|---|---|
create(req) / update(id, req) / get(id) | *ResponseV5 インスタンス | null |
list() 系 | List<*ResponseV5> | 空のリスト(決して null ではない) |
delete(id) | true | false |
exists(id) | true(存在する) | false(存在しない、またはエラー) |
count() 系 | long(≥0) | 0 |
Order ライフサイクル(start/end/pause/resume/abort) | true(成功) | false(遷移不可など) |
例外は throw されません。 HTTP エラー、サーバー ERROR、JSON パース失敗はすべて上表の安全なデフォルト値として吸収されます。エラー原因はデバッグログまたは envelope の
_code/_messageで確認します。
3.2 内部 wire format(参考)
V5 サーバーは以下の envelope 形式でレスポンスを返します。クライアントライブラリが自動的にパースするため、通常の利用時に意識する必要はありません。
// 단건 성공
{ "_status": "OK", "data": { ... } }
// 목록 성공
{ "_status": "OK", "data": [ { ... }, { ... } ] }
// 목록 — items 키 안에 배열
{ "_status": "OK", "data": { "items": [ ... ] } }
// 에러
{
"_status": "ERROR",
"_code": "E1002",
"_message": "validation failed: ...",
"_http_status": 400
}
3.3 V5 ErrorCode マッピング
サーバーが ERROR レスポンスを返す際、_code フィールドに以下のコードが入ります。
| コード | 意味 | 一般的な原因 |
|---|---|---|
E1001 | INVALID_INPUT | 必須フィールドの欠落、型の不一致 |
E1002 | VALIDATION_FAILED | ID 接頭辞違反(SITE_ など) |
E1100 | UNAUTHORIZED | api_key の欠落または無効 |
E1101 | FORBIDDEN | 権限不足 |
E1200 | CONFLICT | ID の重複、FK 競合 |
E1300 | NOT_FOUND | 該当 ID なし |
E1400 | DOMAIN_RULE_VIOLATION | Order の状態遷移不可など |
E1500 | DEPENDENCY_FAILED | 外部システムの応答失敗 |
E1900 | INTERNAL_ERROR | サーバー内部エラー |
3.4 エラー情報の取得
V5 のサービスメソッドは envelope を直接公開しないため、エラー詳細は以下の 2 つの方法で確認します。
方法 1 — デバッグモード
APIClient_V5 client = new APIClient_V5(proto, host, port, user, token, true);
client.connect();
SiteResponseV5 result = client.site().create(req);
if (result == null) {
// 콘솔 로그에서 envelope 확인 가능
System.err.println("등록 실패 — 디버그 로그 확인");
}
コンソール出力:
[V5] POST /api/v5/site body={...}
[V5] response: 400 {"_status":"ERROR","_code":"E1002","_message":"site_id must start with SITE_"}
方法 2 — BaseServiceV5 の static メソッドを使用(カスタム拡張)
BaseServiceV5 のヘルパーは JSONObject レスポンスからエラー情報を抽出します。ライブラリを拡張して envelope をキャプチャするカスタムサービスを作成する際に有用です。
import plantpulse.api.v5.service.BaseServiceV5;
import plantpulse.json.JSONObject;
// 환경 (envelope) JSONObject 가 있다면
String code = BaseServiceV5.getErrorCode(env); // 예: "E1002"
String message = BaseServiceV5.getErrorMessage(env); // 예: "validation failed: ..."
int httpStatus = BaseServiceV5.getHttpStatus(env); // 예: 400
3.5 安全な呼び出しパターン
CRUD 呼び出し
import plantpulse.api.v5.dto.request.SiteRequestV5;
import plantpulse.api.v5.dto.response.SiteResponseV5;
SiteRequestV5 req = new SiteRequestV5();
req.setSite_id("SITE_DJ");
req.setSite_name("대전공장");
SiteResponseV5 created = client.site().create(req);
if (created == null) {
// 실패 처리 — 예: 알람, 재시도, 다른 경로 시도
log.warn("사이트 생성 실패");
return;
}
System.out.println("등록 완료: " + created.getSite_id());
一覧取得の呼び出し
List<SiteResponseV5> sites = client.site().list();
// 실패해도 null 이 아님 — 그대로 순회 가능
for (SiteResponseV5 s : sites) {
System.out.println(s.getSite_id());
}
if (sites.isEmpty()) {
log.info("등록된 사이트 없음");
}
存在確認 → 照会
if (client.site().exists("SITE_DJ")) {
SiteResponseV5 site = client.site().get("SITE_DJ");
// ...
}
Order ライフサイクル(boolean を返す)
String orderId = "ORD_20260514_001";
if (!client.order().start(orderId)) {
// 전이 불가 (예: 이미 START 상태)
log.warn("작업 시작 실패 — 현재 상태 확인");
return;
}
// 작업 진행 ...
if (!client.order().end(orderId)) {
log.warn("작업 종료 실패");
}
3.6 リトライパターン
V5 は自動リトライを行いません。一時的なネットワークエラーに備えたリトライが必要な場合は、個別に実装してください。
import java.util.function.Supplier;
public static <T> T callWithRetry(Supplier<T> apiCall, int maxRetries, long baseDelayMs) {
int attempt = 0;
while (true) {
T result = apiCall.get();
if (result != null) return result; // 성공
if (++attempt >= maxRetries) return null; // 포기
try {
Thread.sleep(baseDelayMs * attempt); // 지수 백오프
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
return null;
}
}
}
// 사용
SiteResponseV5 site = callWithRetry(
() -> client.site().get("SITE_DJ"),
3,
1_000L);
3.7 空レスポンスと null レスポンスの区別
V5 における null または false は、以下の 2 つの意味の両方を含みます。
- サーバーにデータが存在しない(
NOT_FOUND、E1300) - 呼び出しが失敗した(ネットワークエラー、検証失敗など)
これを区別するには exists() を併用してください。
if (!client.site().exists("SITE_DJ")) {
log.info("사이트가 등록되어 있지 않습니다");
// 신규 등록 로직
} else {
SiteResponseV5 site = client.site().get("SITE_DJ");
if (site == null) {
log.error("사이트는 존재하지만 조회 실패 — 일시적 오류 가능");
}
}
次のステップ
- System サービス — ヘルスチェック
- Site サービス — マスタデータの起点