むかしむかしのプロジェクトで、私は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
@QueryParam
@RequestBody
@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サーバーのサンプルコードは、 ここから入手できます 。