Nettyのマッピングリクエスト

むかしむかしのプロジェクトで、私はNettyでhttp-requestsの処理を行う必要がありました。 残念ながら、 Nettyで httpリクエストをマッピングするための標準的な便利なメカニズムはありませんでした(したがって、このフレームワークはまったくありません)。したがって、独自のメカニズムを実装することにしました。







読者がプロジェクトの運命について心配し始めた場合、それは価値がありません。 将来的には、独自の自転車を使用せずに、RESTfulサービス向けにさらに強化されたフレームワークでWebサービスを書き換えることが決定されました。 しかし、成果は残っており、誰かに役立つ可能性があるので、共有したいと思います。

Nettyは、高性能ネットワークアプリケーションを開発するためのフレームワークです。 プロジェクトのウェブサイトで詳細を読むことができます。

Nettyはソケットサーバーを作成するための非常に便利な機能を提供しますが、RESTサーバーを作成するためには、この機能はあまり便利ではありません。







標準のNettyエンジンを使用したリクエスト処理



Nettyでリクエストを処理するには、 ChannelInboundHandlerAdapter



クラスから継承し、 channelRead



メソッドをオーバーライドする必要があります。







 public class HttpMappingHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { } }
      
      





http要求の処理に必要な情報を取得するには、 msg



オブジェクトをHttpRequest



にキャストできます。







 HttpRequest request = (HttpRequest) msg;
      
      





その後、このリクエストから情報を取得できます。 たとえば、リクエストのURL。







 String uri = request.uri();
      
      





リクエストのタイプ。







 HttpMethod httpMethod = request.method();
      
      





そしてコンテンツ。







 ByteBuf byteBuf = ((HttpContent) request).content();
      
      





コンテンツは、たとえば、 POST



要求の本文で渡されるjsonである場合があります。 ByteBuf



Nettyライブラリのクラスであるため、jsonパーサーは動作しそうにありませんが、非常に簡単に文字列にキャストできます。







 String content = byteBuf.toString(StandardCharsets.UTF_8);
      
      





それは、一般に、すべてです。 上記の方法を使用して、http要求を処理できます。 channelRead



、すべてを1か所、つまりchannelRead



メソッドで処理する必要があります。 リクエストを処理するロジックを異なるメソッドとクラスに分けても、URLをこれらのメソッドに1か所でマップする必要があります。







リクエストマッピング



ご覧のとおり 、標準のNetty機能を使用してhttp要求のマッピングを実装することが可能です。 確かに、あまり便利ではありません。 どういうわけか、HTTPリクエストの処理を異なる方法で完全に分離したいと思います(たとえば、 Springで行われているように)。 リフレクションの助けを借りて、同様のアプローチを実装しようとしました。 numライブラリそこから判明しました。 ソースコードはこちらにあります

numライブラリを使用してリクエストマッピングを使用するには、 AbstractHttpMappingHandler



クラスから継承するだけで十分です。その後、このクラスでリクエストハンドラメソッドを作成できます。 これらのメソッドの主な要件は、それらがFullHttpResponse



またはその子孫を返すことFullHttpResponse



。 アノテーションを使用して、このメソッドが呼び出されるHTTPリクエストを表示できます。









注釈名は、呼び出される要求のタイプを示します。 GET



POST



PUT



およびDELETE



4種類のリクエストがサポートされています。 注釈のvalue



パラメーターとして、URLを指定する必要があります。アクセスすると、目的のメソッドが呼び出されます。







文字列Hello, world!



を返すGET



ハンドラーの例 。







 public class HelloHttpHandler extends AbstractHttpMappingHandler { @Get("/test/get") public DefaultFullHttpResponse test() { return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, OK, Unpooled.copiedBuffer("Hello, world!", StandardCharsets.UTF_8)); } }
      
      





リクエストパラメータ



リクエストからハンドラーメソッドへのパラメーターの受け渡しも、アノテーションを使用して実行されます。 これを行うには、次のいずれかの注釈を使用します。









@PathParam



パスパラメータを渡すには、 @PathParam



アノテーションが@PathParam



ます。 アノテーションのvalue



パラメーターとして使用する場合、パラメーター名を指定する必要があります。 さらに、リクエストURLでパラメータ名も指定する必要があります。







GET



ハンドラーがどのように見えるか、 id



パスパラメーターが渡され、このパラメーターが返す例。







 public class HelloHttpHandler extends AbstractHttpMappingHandler { @Get("/test/get/{id}") public DefaultFullHttpResponse test(@PathParam(value = "id") int id) { return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, OK, Unpooled.copiedBuffer(id, StandardCharsets.UTF_8)); } }
      
      





@QueryParam



クエリパラメータを渡すには、 @QueryParam



アノテーションが@QueryParam



ます。 アノテーションのvalue



パラメーターとして使用する場合、パラメーター名を指定する必要があります。 パラメーターのバインドは、 required



注釈パラメーターを使用して制御できます。







GET



ハンドラーがどのように見えるか、クエリパラメーターmessage



渡され、このパラメーターを返す例。







 public class HelloHttpHandler extends AbstractHttpMappingHandler { @Get("/test/get") public DefaultFullHttpResponse test(@QueryParam(value = "message") String message) { return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, OK, Unpooled.copiedBuffer(message, StandardCharsets.UTF_8)); } }
      
      





@RequestBody



POST



リクエストの本文を送信するには、 @RequestBody



アノテーションを@RequestBody



ます。 したがって、 POST



要求でのみ使用できます。 JSON形式のデータがリクエスト本文として送信されることを前提としています。 したがって、 @RequestBody



を使用するには、 JsonParser



インターフェイスの実装をハンドラークラスコンストラクターに渡す必要があります。ハンドラークラスコンストラクターは、要求本文からのデータの解析を行います。 また、ライブラリにはすでにJsonParserDefault



デフォルト実装があります。 この実装では、パーサーとしてjackson



を使用します。







リクエストボディがあるPOST



リクエストハンドラの外観の例。







 public class HelloHttpHandler extends AbstractHttpMappingHandler { @Post("/test/post") public DefaultFullHttpResponse test(@RequestBody Message message) { return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, OK, Unpooled.copiedBuffer("{id: '" + message.getId() +"', msg: '" + message.getMessage() + "'}", StandardCharsets.UTF_8)); } }
      
      





Message



クラスは次のとおりです。







 public class Message { private int id; private String message; public Message() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
      
      





エラー処理



リクエストの処理中にException



が発生し、ハンドラーメソッドのコードでインターセプトされなかった場合、500番目のコードの回答が返されます。 エラー処理のロジックを作成するには、ハンドラークラスでexceptionCaught



メソッドを再定義するだけで十分です。







 public class HelloHttpHandler extends AbstractHttpMappingHandler { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause); ctx.writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR)); } }
      
      





おわりに



それは、一般に、すべてです。 これが興味深く、誰かに役立つことを願っています。










numライブラリを使用したNetty httpサーバーのサンプルコードは、 ここから入手できます








All Articles