netty实现http协议服务

前面了解了netty的启动流程,实现http协议服务在pipeline里添加对应的http协议报文处理器即可。

server端:

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workGroup = new NioEventLoopGroup(4);

ServerBootstrap bootstrap = new ServerBootstrap().group(bossGroup,workGroup)
        .channel(NioServerSocketChannel.class)
        .option(ChannelOption.SO_BACKLOG, 1024)
        .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline p = ch.pipeline();
                p.addLast(new HttpServerCodec());
                p.addLast(new HttpObjectAggregator(65536));
                p.addLast(new CustomHttpServerHandler());
            }
        });
bootstrap.bind(8080).sync();

这里添加了三个编解码处理handler。HttpServerCodec这个是http协议格式处理器,其相当于HttpRequestDecoder和HttpResponseEncoder一对编解码处理器。HttpObjectAggregator是用来报文数据聚合,合并请求数据成一个报文。CustomHttpServerHandler这个是我们自定义的报文处理器,

public class CustomHttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
        String content = "Hello";
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
                Unpooled.wrappedBuffer(content.getBytes()));
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
        ctx.writeAndFlush(response);
    }
}

这里收到消息经过前面两个handler处理后类型是FullHttpRequest,在channelRead0方法里构造了一个简单的FullHttpResponse对象,返回数据给客户端。

这样访问http://localhost:8080就会看到服务端返回的hello。

上面例子只是做了一个简单的请求响应。没有对请求数据进行解析,也没有根据资源uri进行转发不同处理。

请求地址可以从入参FullHttpRequest msg中获取

HttpMethod method = msg.method();
System.out.println("接收请求:"+msg.uri()+"请求方法:"+msg.method().name());

这里uri的dispatch就不处理了,主要是了解报文数据处理。下面来看不同报文格式数据如何处理。同样的这里也只考虑POST请求。

我们平时web开发知道,POST传送数据有好几种方式,其报文数据格式是不一样的。不同数据格式可以从请求报文头的Content-type获取。

1、form表单形式(application/x-www-form-urlencoded)

这种方式参数还是key=value&key=value形式放到请求body中传过来的,可以使用HttpPostRequestDecoder来进行解析.

            HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), msg);
            List<InterfaceHttpData> postData = decoder.getBodyHttpDatas();

            for (InterfaceHttpData data : postData) {
                if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
                    // 处理表单字段
                    Attribute attribute = (Attribute) data;
                    String fieldName = attribute.getName();
                    String fieldValue = null;
                    try {
                        fieldValue = attribute.getValue();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    System.out.println(fieldName + ": " + fieldValue);
                }
            }

2、包含文件(multipart/form-data)

这种方式有一个boundary来区分不同的表单项。可以用HttpPostMultipartRequestDecoder来解析

HttpPostMultipartRequestDecoder decoder = new HttpPostMultipartRequestDecoder(new DefaultHttpDataFactory(false), msg);
List<InterfaceHttpData> postData = decoder.getBodyHttpDatas();

for (InterfaceHttpData data : postData) {
    if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
        // 处理表单字段
        Attribute attribute = (Attribute) data;
        String fieldName = attribute.getName();
        String fieldValue = null;
        try {
            fieldValue = attribute.getValue();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(fieldName + ": " + fieldValue);
    } else if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) {
        // 处理文件上传字段
        FileUpload fileUpload = (FileUpload) data;
        String fieldName = fileUpload.getName();
        String fileName = fileUpload.getFilename();
        String fileType = fileUpload.getContentType();
        long fileSize = fileUpload.length();
        System.out.println("文件上传属性: " + fieldName + ", 文件名: " + fileName + ", 文件类型: " + fileType + ", 文件大小: " + fileSize + " bytes");
    }
}

这里要注意文件的大小,还记得最开始添加的报文聚合handler:HttpObjectAggregator,如果超过了这个指定的大小会直接返回414错误。

另外像其它如json、xml这种格式,直接获取报文体进行处理即可,不需要特殊的处理

ByteBuf content = msg.content();
String requestBody = content.toString(StandardCharsets.UTF_8);
System.out.println("请求内容:"+requestBody);

请求报文处理完了之后还是最后要构建一个FullHttpResponse对象将响应数据返回给客户端。这里整个处理下来搭建一个完整的http服务还是有许多东西要考虑的,不如直接使用现有的web容器,这里仅仅是为了演示,也为下一篇说websocket做准备。

你可能感兴趣的:(netty,http,网络,netty)