从前,在一个遥远的项目中,我需要在Netty上处理http请求。 不幸的是,在Netty中没有用于映射http请求的标准便捷机制(并且该框架根本不用于此目的),因此,决定实施我们自己的机制。
如果读者开始担心项目的命运,那是不值得的; 将来,我们决定在不使用RESTful服务的情况下,在针对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
。
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
方法中。 即使我们将处理请求的逻辑分为不同的方法和类,您仍然必须将URL映射到这些方法的某个位置。
请求映射
好了,如您所见,可以使用标准的Netty功能实现http请求的映射。 没错,这不会很方便。 我想以某种方式通过不同的方法将http请求的处理完全分开(例如,在Spring中完成)。 在反思的帮助下,尝试实施类似的方法。 num库由此产生。 它的源代码可以在这里找到。
要使用num库使用请求映射,从AbstractHttpMappingHandler
类继承就足够了,之后您可以在此类中创建请求处理程序方法。 这些方法的主要要求是它们返回FullHttpResponse
或其后代。 您可以通过注释显示通过什么http请求来调用此方法:
批注名称指示将调用的请求类型。 支持四种类型的请求: GET
, POST
, PUT
和DELETE
。 作为注释中的value
参数,您必须指定URL,在访问URL时,将调用所需的方法。
GET
处理程序的示例,返回字符串Hello, world!
。
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
批注。 将其用作注释的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
批注。 将其用作注释的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
批注。 因此,只允许在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
并且该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服务器示例代码在此处提供 。