SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Javaプログラマーのための実践「Vert.x」

Vert.xによる簡単なRESTアプリケーションの作成

Javaプログラマーのための実践「Vert.x」 第3回

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"));
      });
   });
}

次のページ
メインモジュールの作成

この記事は参考になりましたか?

Javaプログラマーのための実践「Vert.x」連載記事一覧

もっと読む

この記事の著者

リョウジ(株式会社DMM.comラボ)(リョウジ)

DMM.comのプラットフォームにおける主要アーキテクチャに関しての技術研究開発を担当。 非同期のメッセージ&キューの導入、Vert.xフレームワークを採用し社内の基盤を整備等、 高負荷対策・可用性の高いアーキテクチャの開発・設計など幅広く活躍。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/8712 2015/06/08 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー