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である場合があります。 ByteBufNettyライブラリのクラスであるため、jsonパーサーは動作しそうにありませんが、非常に簡単に文字列にキャストできます。


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

それは、一般に、すべてです。 上記の方法を使用して、http要求を処理できます。 channelRead 、すべてを1か所、つまりchannelReadメソッドで処理する必要があります。 リクエストを処理するロジックを異なるメソッドとクラスに分けても、URLをこれらのメソッドに1か所でマップする必要があります。


リクエストマッピング


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



注釈名は、呼び出される要求のタイプを示します。 GETPOSTPUTおよび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サーバーのサンプルコードは、 ここから入手できます



Source: https://habr.com/ru/post/J435864/


All Articles