Sparkフレームワークを使用して簡単なRESTful APIを作成する

何を学びますか



Java 8機能インターフェースを使用して汎用コントローラーを定義する方法を学習しますGitHubのサンプルコード







EchoApplication.java



これは、アプリケーションを結び付けるクラスです。 このクラスを開くと、すぐにすべてがどのように機能するかを理解する必要があります。







public class EchoApplication { private static final Logger LOG = Logger.getLogger(EchoApplication.class); // Declare dependencies public static EchoService echoService; static { echoService = new EchoService(); } public static void main(String[] args) { port(4567); start(); } public static void start() { JsonUtils.registerModules(); LOG.info("Initializing routes"); establishRoutes(); } private static void establishRoutes() { path("/api", () -> path("/v1", () -> { get(Path.ECHO, EchoController.echo); post(Path.PONG, EchoController.pong); }) ); } }
      
      





静的な依存関係?



静的依存関係は、 Java



で見慣れているものではありませんが、Webアプリケーションのコントローラーに関しては、注入された依存関係よりも静的の方が優れています。 さらに、1つの機能のみを実行するマイクロサービスでは、多くのサービスはありません。 利点のうち、DIを拒否するとアプリケーションの起動が速くなり、1つまたは2つのサービスの単体テストをDIなしで作成できることに注意してください。







パスとController.field



Path.java



クラスPath.java



は、REST APIへのエントリポイントを定数として保持しています。 このアプリケーションでは、1つのEchoController



配置されるリクエストハンドラは2つのみEchoController









 public class EchoController { public static Route echo = (req, res) -> ((QueryParamsHandlerFunc) echoQuery -> echoService .echo(echoQuery.value("echo")) .map(Answer::ok) .orElse(Answer.error(HTTP_BAD_REQUEST, ErrorMessages.UNABLE_TO_ECHO + req.body())) ).handleRequest(req, res); public static Route pong = (req, res) -> ((ModelHandlerFunc<Ping>) pongRequest -> echoService .pong(pongRequest) .map(Answer::ok) .orElse(Answer.error(HTTP_BAD_REQUEST, ErrorMessages.UNABLE_TO_PONG + req.body())) ).handleRequest(req, res, Ping.class); }
      
      





最初のハンドラーはラムダ関数で、その本体はQueryParamsHandlerFunc



機能インターフェイスです。 このインターフェイスの入力パラメーターはGETリクエストデータです。リンクからのリクエストの一部を含む辞書です。 (たとえば、 /echo?echo=message



)。 このインターフェイスの本文では、ハンドラーサービスが呼び出されます。 つまり サービスは既にアセンブルされたオブジェクトを受け取り、コントローラーにまったく依存しないため、テストが容易になります。 サービスは、応答を作成するAnswer



クラスにマップするOptional



返します。 エラーが発生した場合、 Answer



クラスがエラーコードとエラーメッセージとともに返されます。 このインターフェイスにはQueryParamsHandlerFunc::handleRequest



があり、 Func::handlerRequest(req, res)



ハンドラーコントローラーから要求と応答が転送されます。







2番目のハンドラーは、上記とほぼ同じインターフェースを形成する結果を返します。 Payload



インターフェースを継承するクラスのみがテンプレートパラメーターとして指定されます。 このリクエストの処理は、上記のものと変わりません。 唯一の違いはModelHandlerFunc::handleRequest



このインターフェイスのModelHandlerFunc::handleRequest



Payload



クラスをModelHandlerFunc<Ping>



パラメーターとして受け取ることです。







QueryParamsHandlerFunc.java



これは、GET要求ハンドラーに共通のアクションが発行される基本インターフェイスを継承する機能的なインターフェイスです。 このインターフェイスは、デフォルトのメソッドQueryParamsHandlerFunc :: handleRequestを定義します。このメソッドは、入力として要求オブジェクトと応答オブジェクトを受け入れます。 ベースヘッダーメソッドBaseHandlerFunc :: commonCheck(request)を呼び出して、ヘッダーのチェックなどを実行します。 次に、リクエストからクエリディクショナリ(/ echo?Echo = message)を取得し、それらをQueryParamsHandlerFunc ::プロセスインターフェイスで定義されたメソッドに渡し、リクエストを処理した後、応答コードを示し、この応答をJsonでシリアル化します。







 @FunctionalInterface public interface QueryParamsHandlerFunc extends BaseHandlerFunc { default String handleRequest(Request request, Response response) { String check = commonCheck(request); if (StringUtils.isNotBlank(check)) { return check; } QueryParamsMap queryParamsMap = request.queryMap(); Answer processed = process(queryParamsMap); response.status(processed.getCode()); return dataToJson(processed); } Answer process(QueryParamsMap data); }
      
      





ModelHandlerFunc.java



このインターフェースは上記と同じように機能しますが、POSTリクエストを処理することだけを区別しています。 変換されたクラスは、リクエストパラメータを処理するときと同様に、 ModelHandlerFunc::process



インターフェースメソッドに渡されModelHandlerFunc::process









 @FunctionalInterface public interface ModelHandlerFunc<T extends Payload> extends BaseHandlerFunc { default String handleRequest(Request request, Response response, Class<T> clazz) { String check = commonCheck(request); if (StringUtils.isNotBlank(check)) { return check; } String json = request.body(); T data = jsonToData(json, clazz); Answer processed = process(data); response.status(processed.getCode()); return dataToJson(processed); } Answer process(T data); }
      
      





BaseHandlerFunc.java



これは、一般的なメソッドを集約するインターフェースです。







 public interface BaseHandlerFunc { default String commonCheck(Request request) { // do your smart check here return null; } }
      
      





ジャクソン多相型処理アノテーション



AnswerクラスとJSONでのロードをシリアル化するために、@ JsonTypeInfo @JsonTypeInfo



を使用したポリモーフィズムが適用されました。 リンクの詳細。







コントローラーテスト



コントローラーをテストするには、 Spark Testライブラリーを使用します。 サンプルコードのテスト。







 public class EchoControllerTest { private static String echoUrl = "/api/v1"; private static Integer randomPort = 1000 + new Random().nextInt(60000); public static class BoardBoxControllerTestSparkApplication implements SparkApplication { @Override public void init() { EchoApplication.start(); } } @ClassRule public static SparkServer<BoardBoxControllerTestSparkApplication> testServer = new SparkServer<>(BoardBoxControllerTestSparkApplication.class, randomPort); @Test public void should_echo() throws HttpClientException { String echoMsg = "echo"; Echo echo = (Echo) get("/echo?echo" + "=" + echoMsg).getBody(); assertEquals(echoMsg, echo.getEcho()); } @Test public void should_pong() throws HttpClientException { Pong pong = (Pong) post("/ping", new Ping("PING")).getBody(); assertEquals("PING PONG", pong.getPong()); } private Answer post(String path, Object payload) throws HttpClientException { PostMethod resp = testServer.post(echoUrl + path, dataToJson(payload), false); HttpResponse execute = testServer.execute(resp); return jsonToData(new String(execute.body()), Answer.class); } private Answer get(String params) throws HttpClientException { GetMethod resp = testServer.get(echoUrl + "/" + params, false); HttpResponse execute = testServer.execute(resp); return jsonToData(new String(execute.body()), Answer.class); } }
      
      





参照資料






All Articles