RESTインターフェースモジュールの作成
まずはVerticleクラスを継承し、RESTリクエストを受けるモジュールを作成します。
package com.dmm.vertx.example;
import org.vertx.java.platform.Verticle;
public class RestVerticle extends Verticle {
}
必須な実装として、startメソッドをオーバーライドし、HttpServerのインスタンスを作成します。
private EventBus eb;
@Override
public void start(Future<Void> startedResult) {
super.start(startedResult);
this.eb = vertx.eventBus();
HttpServer httpServer = vertx.createHttpServer();
}
このままではHttpServerを起動できていないため、設定からポート番号を取得し、リクエストを処理するためのリクエストハンドラーの登録、指定したポート番号でサーバーの起動を行います。
int portNo = container.config().getInteger("portNo", 8080);
httpServer.requestHandler(req -> {
// TODO
}).listen(portNo, event -> {
if (event.succeeded()) {
eb.send("http.ready", new JsonObject("{\"status\":\"ok\"}")); // デプロイ完了イベント
} else {
container.logger().error("deploy error.", event.cause());
eb.send("http.ready", new JsonObject("{\"status\":\"error\"}")); // デプロイ失敗
}
});
上記のTODO部分に各リクエストの処理を記述していきます。
今回は各HTTPメソッドをあらわすenumクラスを定義し、各HTTPメソッド別に処理を振り分けます。
private enum HttpMethod {
GET,
POST,
PUT,
DELETE
}
HTTPレスポンスのステータスをHTTPメソッド別にenumを定義してしまいます。
private enum RestStatus {
OK(HttpResponseStatus.OK, HttpResponseStatus.CREATED, HttpResponseStatus.OK, HttpResponseStatus.NO_CONTENT),
NG(HttpResponseStatus.NOT_FOUND, HttpResponseStatus.CONFLICT,
HttpResponseStatus.BAD_REQUEST, HttpResponseStatus.NOT_FOUND),
ERROR(HttpResponseStatus.INTERNAL_SERVER_ERROR);
public final HttpResponseStatus getStatus;
public final HttpResponseStatus postStatus;
public final HttpResponseStatus putStatus;
public final HttpResponseStatus deleteStatus;
RestStatus(HttpResponseStatus get, HttpResponseStatus post, HttpResponseStatus put, HttpResponseStatus delete) {
this.getStatus = get;
this.postStatus = post;
this.putStatus = put;
this.deleteStatus = delete;
}
RestStatus(HttpResponseStatus status) {
this(status, status, status, status);
}
}
分かりやすいようにJavaのServletで使用するメソッド名で各HTTPメソッド別の処理を作成してみます。
try {
HttpMethod method = HttpMethod.valueOf(req.method());
switch (method) {
case GET:
doGet(req);
break;
case DELETE:
doDelete(req);
break;
case POST:
doPost(req);
break;
case PUT:
doPut(req);
break;
default: // ritual
break;
}
} catch (IllegalArgumentException e) {
error(HttpResponseStatus.METHOD_NOT_ALLOWED, req.response());
} catch (Throwable t) {
error(HttpResponseStatus.INTERNAL_SERVER_ERROR, req.response());
}
private void error(HttpResponseStatus status, HttpServerResponse res) {
this.reply(status, res, null);
}
private void reply(HttpResponseStatus status, HttpServerResponse res, Object resBody) {
res.setStatusCode(status.code());
res.setStatusMessage(status.reasonPhrase());
if (status != HttpResponseStatus.NO_CONTENT && resBody != null) {
res.headers().set(HttpHeaders.Names.CONTENT_TYPE, "application/json; charset=UTF-8");
res.end(resBody.toString());
} else {
res.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=UTF-8");
res.end();
}
}
doGetの実装をします。この処理ではHttpServerRequestをJsonObjectに変換して、"http.get"というイベントとしてリクエストメッセージを送信し、イベント応答をHttpServerResponseに設定してレスポンスを返すことで完了します。先にパラメータをJsonObjectに変換するためのメソッドも定義します。
private JsonObject paramsToJson(HttpServerRequest req) {
JsonObject params = new JsonObject();
for (Map.Entry<String, String> entry : req.params()) {
params.putValue(entry.getKey(), entry.getValue());
}
return params;
}
private void doGet(final HttpServerRequest req) {
JsonObject params = this.paramsToJson(req);
this.eb.send("http.get", params, (Message<JsonObject> event) -> {
RestStatus stat = RestStatus.valueOf(event.body().getString("status").toUpperCase());
reply(stat.getStatus, req.response(), event.body().getValue("result"));
});
}
doPostの実装も行います。ServletのようにGETでもPOSTでも同じインターフェースでパラメータを取得することはできません。POSTの場合、HttpServerRequestのparams()でパラメータを受け取ることができず、リクエストのボディ部で渡ってくるため、bodyHandlerでパラメータを受け取る処理を記述する必要があります。
GETと同様に、先にボディ部のパラメータをJsonObjectに変換するためのメソッドも定義します。その後の処理はdoGetとほぼ同様になります。
private JsonObject paramsToJson(Buffer body) {
JsonObject params = new JsonObject();
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(body.toString(), false);
for (Map.Entry<String, List<String>> entry : queryStringDecoder.parameters().entrySet()) {
List<String> list = entry.getValue();
if (list.size() == 1) params.putString(entry.getKey(), list.get(0));
else params.putArray(entry.getKey(), new JsonArray(list));
}
return params;
}
private void doPost(final HttpServerRequest req) {
req.bodyHandler(body -> {
JsonObject params = this.paramsToJson(body);
eb.send("http.post", params, (Message<JsonObject> event) -> {
RestStatus stat = RestStatus.valueOf(event.body().getString("status").toUpperCase());
reply(stat.postStatus, req.response(), event.body().getValue("result"));
});
});
}
同じように doDelete、doPutメソッドの実装を行います。
private void doDelete(HttpServerRequest req) {
JsonObject request = this.paramsToJson(req);
this.eb.send("http.delete", request, (Message<JsonObject> event) -> {
RestStatus stat = RestStatus.valueOf(event.body().getString("status").toUpperCase());
reply(stat.deleteStatus, req.response(), event.body().getValue("result"));
});
}
private void doPut(HttpServerRequest req) {
req.bodyHandler(body -> {
JsonObject request = this.paramsToJson(body);
eb.send("http.put", request, (Message<JsonObject> event) -> {
RestStatus stat = RestStatus.valueOf(event.body().getString("status").toUpperCase());
reply(stat.putStatus, req.response(), event.body().getValue("result"));
});
});
}
