【九】Netty HTTP+XML协议栈开发

介绍

由于HTTP协议的通用性,很多异构系统间的通信交互采用HTTP协议,通过HTTP协议承载业务数据进行消息交互,例如非常流行的HTTP+XML 或者 RESTful +JSON。在Java领域,最常用的HTTP协议栈就是基于Servlet规范的Tomcat Web容器和Jetty等轻量级容器。但是,很多基于HTTP的应用都是后台应用,HTTP仅仅是承载数据交换的一个通道,是一个载体而不是容器,在这种场景下,一般不需要类似Tomcat这样重量型Web容器。
下面我们就利用Netty提供的基础HTTP协议栈功能,来扩展开发HTTP+XML协议栈。

业务场景

模拟简单的用户订购系统。客户端填写订单,通过HTTP客户端像服务端发送订购请求,请求消息放在HTTP消息体中,以XML承载,即采用 HTTP+XML方式进行通信。HTTP服务端接收到订购请求后,对订单进行修改,然后通过HTTP+XML的方式返回应答消息。双方采用HTTP1.1协议。

流程图

在这里插入图片描述

技术栈设计

流程图的分析:

Step 1

构造订购请求消息并将其编码为HTTP+XML形式.Netty 的HTTP协议栈提供了构造HTTP请求消息的相关接口。但是无法将普通的POJO对象转换为HTTP+XML 的HTTP请求消息。需要自定义开发HTTP+XML 格式的消息编码器

Step2

利用Netty 的HTTP协议栈,可以支持HTTP链路的建立和请求消息的发送。Netty支持,不需要开发

Step3

HTTP服务端要将HTTP+XML 格式的订购请求消息解码为订购请求的POJO对象。同时获取HTTP请求消息头信息。利用Netty的HTTP 请求消息的解码。但是,如果消息体为XML格式,Netty 无支持将其解码为POJO对象,需要在Netty协议栈的基础上面进行开发。

Step4

服务端对订购请求消息处理完成后,重新将其封装成功XML。通过HTTP应答消息体携带给客户端。Netty的HTTP协议栈不支持直接将POJO对象的应答消息以XML方式发送。需要自定义开发。

Step5

HTTP 客户端需要将HTTP+XML格式的应答消息解码为订购POJO对象,同时能够获取应答消息的HTTP头消息。Netty的协议栈不支持自动的消息解码。需要自定义开发。

分析结果

对于HTTP的编码和解码。Netty已经实现了,我们可以直接拿来用。
如果不清楚Netty 实现HTTP服务,可以查看相关的博客文章:
https://blog.csdn.net/echohuangshihuxue/article/details/128634868;
https://blog.csdn.net/echohuangshihuxue/article/details/128618860;
https://blog.csdn.net/echohuangshihuxue/article/details/128632486;

但是HTTP+XML 请求消息和应答消息的解码和编码,目前Netty 不支持,需要我们手动开发。
下面我们就是自己开发 这些编码器和解码器来实现之前的业务场景。

开发

有了之前的分析,我们现在的开发重点其实就是开发出对应的编码器和解码器。然后实现HTTP+XML 的自动编码和解码功能。整个流程就能在Netty 框架的基础上面实现了。

开发流程图

在这里插入图片描述

代码

jar 依赖

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId> <!-- Use 'netty5-all' for 5.0-->
            <version>5.0.0.Alpha1</version>
            <scope>compile</scope>
        </dependency>

      <dependency>
            <groupId>org.jboss.marshalling</groupId>
            <artifactId>jboss-marshalling</artifactId>
            <version>1.4.11.Final</version>
        </dependency>

        <dependency>
            <groupId>org.jboss.marshalling</groupId>
            <artifactId>jboss-marshalling-serial</artifactId>
            <version>1.4.11.Final</version>
        </dependency>
        <dependency>
            <groupId>org.jibx</groupId>
            <artifactId>jibx-run</artifactId>
            <version>1.4.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.jibx/jibx-extras -->
        <dependency>
            <groupId>org.jibx</groupId>
            <artifactId>jibx-extras</artifactId>
            <version>1.4.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.jibx/jibx-bind -->
        <dependency>
            <groupId>org.jibx</groupId>
            <artifactId>jibx-bind</artifactId>
            <version>1.4.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.jibx/jibx-tools -->
        <dependency>
            <groupId>org.jibx</groupId>
            <artifactId>jibx-tools</artifactId>
            <version>1.4.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.jibx/jibx-schema -->
        <dependency>
            <groupId>org.jibx</groupId>
            <artifactId>jibx-schema</artifactId>
            <version>1.4.2</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.apache.bcel/bcel -->
        <dependency>
            <groupId>org.apache.bcel</groupId>
            <artifactId>bcel</artifactId>
            <version>6.7.0</version>
        </dependency>

代码结构如图

在这里插入图片描述

pojo 包

public class Address {
    /**
     * First line of street information (required).
     */
    private String street1;

    /**
     * Second line of street information (optional).
     */
    private String street2;

    private String city;

    /**
     * State abbreviation (required for the U.S. and Canada, optional otherwise )
     */

    private String state;

    /**
     * Postal code (required for the U.S. and Canada ,optional otherwise ).
     */
    private String postCode;

    /**
     * Country name (optional ,U.S. assumed if not supplied).
     */
    private String country;


    public String getStreet1() {
        return street1;
    }

    public void setStreet1(String street1) {
        this.street1 = street1;
    }

    public String getStreet2() {
        return street2;
    }

    public void setStreet2(String street2) {
        this.street2 = street2;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getPostCode() {
        return postCode;
    }

    public void setPostCode(String postCode) {
        this.postCode = postCode;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    @Override
    public String toString() {
        return "Address{" +
                "street1='" + street1 + '\'' +
                ", street2='" + street2 + '\'' +
                ", city='" + city + '\'' +
                ", state='" + state + '\'' +
                ", postCode='" + postCode + '\'' +
                ", country='" + country + '\'' +
                '}';
    }
}

public class Customer {
    private long customerNumber;
    /**
     * Personal name
     */
    private String firstName;

    /**
     * Family name .
     */
    private String lastName;

    /**
     * Middle name(s) if any.
     */
    private List<String> middleNames;


    public long getCustomerNumber() {
        return customerNumber;
    }

    public void setCustomerNumber(long customerNumber) {
        this.customerNumber = customerNumber;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public List<String> getMiddleNames() {
        return middleNames;
    }

    public void setMiddleNames(List<String> middleNames) {
        this.middleNames = middleNames;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "customerNumber=" + customerNumber +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", middleNames=" + middleNames +
                '}';
    }
}
public class Order {
    private long orderNumber;
    private Customer customer;

    /**
     * Billing address information
     */
    private Address bilTo;

    private String shipping;

    /**
     * Shipping address information. If missing ,the billing address is also
     * used as the shipping address.
     */
    private Address shipTo;

    private Float total;

    public long getOrderNumber() {
        return orderNumber;
    }

    public void setOrderNumber(long orderNumber) {
        this.orderNumber = orderNumber;
    }

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    public Address getBilTo() {
        return bilTo;
    }

    public void setBilTo(Address bilTo) {
        this.bilTo = bilTo;
    }

    public String getShipping() {
        return shipping;
    }

    public void setShipping(String shipping) {
        this.shipping = shipping;
    }

    public Address getShipTo() {
        return shipTo;
    }

    public void setShipTo(Address shipTo) {
        this.shipTo = shipTo;
    }

    public Float getTotal() {
        return total;
    }

    public void setTotal(Float total) {
        this.total = total;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderNumber=" + orderNumber +
                ", customer=" + customer +
                ", bilTo=" + bilTo +
                ", shipping=" + shipping +
                ", shipTo=" + shipTo +
                ", total=" + total +
                '}';
    }
}

public class OrderFactory {
    public static Order create(int number){
        Order order=new Order();
        //orderNumber
        order.setOrderNumber(number);
        //total
        order.setTotal(9999.999f);
        //customer
        Customer customer=new Customer();
        customer.setFirstName("echo");
        customer.setLastName("tong");
        List<String>list=new ArrayList<>();
        list.add("666");
       // list.add("555");
        customer.setCustomerNumber(123);
        //customer.setMiddleNames(list);
        order.setCustomer(customer);

        //billTo
        Address billTo=new Address();
        billTo.setStreet1("西乡大道");
        billTo.setCity("深圳市");
        billTo.setState("广东省");
        billTo.setPostCode("123321");
        billTo.setCountry("中国");
        order.setBilTo(billTo);
        //shipping
        order.setShipping(Shipping.INTERNATIONAL_MAIL);
        //shipTo
        Address shipTo=new Address();
        shipTo.setStreet1("西乡大道");
        shipTo.setCity("深圳市");
        shipTo.setState("广东省");
        shipTo.setPostCode("123321");
        shipTo.setCountry("中国");
        order.setShipTo(shipTo);


        return order;
    }
}
public class Shipping {
    public final static String STANDARD_MAIL="STANDARD_MAIL";
    public final static String PRIORITY_MAIL="PRIORITY_MAIL";
    public final static String INTERNATIONAL_MAIL="StringINTERNATIONAL_MAIL";
    public final static String DOMESTIC_EXPRESS="DOMESTIC_EXPRESS";
    public final static String INTERNATIONAL_EXPRESS="INTERNATIONAL_EXPRESS";
}

request包

/**
 * 包含两个成员变量
 * FullHttpRequest
 * 编码对象 Object
 */
public class HttpXmlRequest {
    private FullHttpRequest request;
    private Object body;

    public HttpXmlRequest(FullHttpRequest request, Object body) {
        this.request = request;
        this.body = body;
    }

    public FullHttpRequest getRequest() {
        return request;
    }

    public void setRequest(FullHttpRequest request) {
        this.request = request;
    }

    public Object getBody() {
        return body;
    }

    public void setBody(Object body) {
        this.body = body;
    }

    @Override
    public String toString() {
        return "HttpXmlRequest{" +
                "request=" + request +
                ", body=" + body +
                '}';
    }
}
public class HttpXmlRequestDecoder extends AbstractHttpXmlDecoder<FullHttpRequest> {
    public HttpXmlRequestDecoder(Class<?> clazz) {
        this(clazz,false);
    }

    public HttpXmlRequestDecoder(Class<?> clazz,boolean isprint){
        super(clazz,isprint);
    }



    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, FullHttpRequest request,
                          List<Object> list) throws Exception {
        //如果解码失败,返回400
        if (!request.getDecoderResult().isSuccess()){
            sendError(channelHandlerContext,HttpResponseStatus.BAD_REQUEST);
            return;
        }
        //通过反序列化得到的ByteBuf来构造 HttpXmlRequest对象。
        HttpXmlRequest request1=new HttpXmlRequest(request,
                //反序列化Request 里面的内容。
                decode0(channelHandlerContext,request.content()));
        //添加到解码结果list列表
        list.add(request1);
    }


    private static void sendError(ChannelHandlerContext context, HttpResponseStatus status){
        //构造错误返回体
        FullHttpResponse response=new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
                status, Unpooled.copiedBuffer("Failure: "+status.toString()+"\r\n",
                CharsetUtil.UTF_8));
        response.headers().set(CONTENT_TYPE,"text/plain; charset=UTF-8");
        context.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }
}
public class HttpXmlRequestEncoder extends AbstractHttpXmlEncoder<HttpXmlRequest> {
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext,
                          HttpXmlRequest httpXmlRequest, List<Object> list) throws Exception {
        //调用父类的encode0,将业务需要发生的POJO对象Order实例通过 JiBx 序列化为XML,
        //随后转换为Netty的ByteBuf对象
        ByteBuf body = encode0(channelHandlerContext, httpXmlRequest.getBody());
        FullHttpRequest request = httpXmlRequest.getRequest();
        //request 如果为空,初始化一个request,并设置请求头
        if (request == null) {
            //此处采用了硬编码方式,如果要做成产品话,可以做成XML配置文件,
            // 允许业务自定义配置,提升定制的灵活性
            request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/do", body);
            HttpHeaders httpHeaders = request.headers();
            //设置host
            httpHeaders.set(HttpHeaders.Names.HOST, InetAddress.getLocalHost());
            //设置客户端可接受的内容编码
            httpHeaders.set(HttpHeaders.Names.ACCEPT_ENCODING,
                    HttpHeaders.Values.GZIP.toString() + "," + HttpHeaders.Values.DEFLATE.toString());
            //设置客户端可接受的字符集
            httpHeaders.set(HttpHeaders.Names.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
            //设置客户端可接受的语言
            httpHeaders.set(HttpHeaders.Names.ACCEPT_LANGUAGE, "zh");
            //设置 客户端的操作系统
            httpHeaders.set(HttpHeaders.Names.USER_AGENT, "Netty xml Http Client side");
            //设置客户端接收哪些类型
            httpHeaders.set(HttpHeaders.Names.ACCEPT,
                    "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        }
        //设置请求字节长度
        //由于请求消息消息体不为空,也没有使用Chunk方式,所以在HTTP消息头中设置消息体的长度Content-Length.
        HttpHeaders.setContentLength(request, body.readableBytes());
        //完成消息体的XML序列化后将重新构造的HTTP请求消息加入到list,
        // 由后续Netty的HTTP请求编码器继续对HTTP请求消息进行编码
        list.add(request);
    }
}

response 包

public class HttpXmlResponse {
    private FullHttpResponse httpResponse;
    private Object result;

    public HttpXmlResponse(FullHttpResponse httpResponse, Object result) {
        this.httpResponse = httpResponse;
        this.result = result;
    }

    public FullHttpResponse getHttpResponse() {
        return httpResponse;
    }

    public void setHttpResponse(FullHttpResponse httpResponse) {
        this.httpResponse = httpResponse;
    }

    public Object getResult() {
        return result;
    }

    public void setResult(Object result) {
        this.result = result;
    }

    @Override
    public String toString() {
        return "HttpXmlResponse{" +
                "httpResponse=" + httpResponse +
                ", result=" + result +
                '}';
    }
}
public class HttpXmlResponseDecoder extends AbstractHttpXmlDecoder<DefaultFullHttpResponse> {

    public HttpXmlResponseDecoder(Class<?> clazz){
        this(clazz,false);
    }

    public HttpXmlResponseDecoder(Class<?> clazz,boolean isPrint){
        super(clazz,isPrint);
    }
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext,
                          DefaultFullHttpResponse defaultFullHttpResponse,
                          List<Object> list) throws Exception {
        //1.利用基类的decode0方法 解码响应信息
        //2.构造HttpXmlResponse 对象
        //3.添加到解码结果列表中。供后续继续解码
        HttpXmlResponse response =new HttpXmlResponse(defaultFullHttpResponse,
                decode0(channelHandlerContext,defaultFullHttpResponse.content()));
        list.add(response);
    }
}
public class HttpXmlResponseEncoder extends AbstractHttpXmlEncoder<HttpXmlResponse> {
    @Override
    protected void encode(ChannelHandlerContext context,
                          HttpXmlResponse responseMsg, List<Object> list) throws Exception {
        ByteBuf body = encode0(context, responseMsg.getResult());
        FullHttpResponse response = responseMsg.getHttpResponse();
        if (response == null) {
            //如果业务侧没有构造HttpResponse,则构造一个
            response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, body);
        } else {
            //如果业务侧已经构造了一个,但是由于 Netty的DefaultFullResponse 没有提供动态设置消息体content的接口。
            //只能在第一次构造的时候设置content。
            // 所以现在为了添加内容,只能是舍弃原先的FullHttpResponse,重新再构造一个。
            response = new DefaultFullHttpResponse(responseMsg.getHttpResponse().getProtocolVersion(),
                    responseMsg.getHttpResponse().getStatus(), body);
        }
        //消息头设置消息体内容格式
        response.headers().set(CONTENT_TYPE,"text/html");
        //消息头中设置消息体的长度
        setContentLength(response,body.readableBytes());
        //将编码后的DefaultFullHttpResponse 对象添加到编码结果列表中,
        //由后续Netty的HTTP编码类进行二次编码
        list.add(response);
    }
}

client 包

public class HttpXmlClient {

    public void connect(String host,int port){
        EventLoopGroup loopGroup=new NioEventLoopGroup();
        try {
            Bootstrap bootstrap=new Bootstrap();
            bootstrap.group(loopGroup)
                    .option(ChannelOption.TCP_NODELAY,true)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                                // HttpRequestEncoder 负责将二进制码流解码成为HTTP的应答消息
                                socketChannel.pipeline().
                                        addLast("http-decoder",new HttpResponseDecoder());
                                //HttpObjectAggregator 负责将一个HTTP请求消息的多个部分合并成一条完整的HTTP信息
                                socketChannel.pipeline()
                                        .addLast("http-aggregator",new HttpObjectAggregator(65536));
                                //xml解码器,实现HTTP+XML应答消息的自动解码
                                socketChannel.pipeline()
                                        .addLast("xml-decoder",new HttpXmlResponseDecoder(Order.class,true));
                                // HttpRequestEncoder 将POJO对象 编码为 HTTP接收的消息
                                socketChannel.pipeline()
                                        .addLast("http-encoder",new HttpRequestEncoder());
                                //HttpXmlRequestEncoder xml 编码器,实现HTTP-XML 请求消息的自动编码
                                socketChannel.pipeline()
                                        .addLast("xml-encoder",new HttpXmlRequestEncoder());
                                //自定义的业务逻辑处理类
                                socketChannel.pipeline()
                                        .addLast(new HttpXmlClientHandler());
                        }
                    });
            //发起异步链接操作
            ChannelFuture future=bootstrap.connect(host,port).sync();
            System.out.println("HTTP-XML client is stated..... and do connect with server ");
            //等待客户端链路关闭
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            //优雅退出,释放NIO线程组.
            loopGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        int port=8080;
        new HttpXmlClient().connect("127.0.0.1",port);
    }
}
public class HttpXmlClientHandler extends SimpleChannelInboundHandler<HttpXmlResponse> {

    public void channelActive(ChannelHandlerContext context){
        //链接成功的时候,给服务端发送HttpXmlRequest对象
        //编码器会自动完成编码
        HttpXmlRequest request=new HttpXmlRequest(null, OrderFactory.create(123));
        context.writeAndFlush(request);
    }


    @Override
    protected void messageReceived(ChannelHandlerContext channelHandlerContext, HttpXmlResponse response) throws Exception {
        //此时接收到的信息 已经是自动解码后的HttpXmlResponse对象了
        System.out.println("The client receive response of http header is : "+response.getHttpResponse().headers().names());
        System.out.println("The client receive response of http body is : "+response.getResult());
    }
}

server包

public class HttpXmlServer {

    public void run(int port){
        EventLoopGroup boosGroup=new NioEventLoopGroup();
        EventLoopGroup workerGroup=new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap=new ServerBootstrap();
            bootstrap.group(boosGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            //HttpRequestDecoder 负责将二进制码流解码成为HTTP的请求消息
                            socketChannel.pipeline()
                                    .addLast("http-decoder",new HttpRequestDecoder());
                            //HttpObjectAggregator 负责将一个HTTP请求消息的多个部分合并成一条完整的HTTP信息
                            socketChannel.pipeline()
                                    .addLast("http-aggregator",new HttpObjectAggregator(655326));
                            //xml解码器,实现HTTP+XML请求消息的自动解码
                            socketChannel.pipeline()
                                    .addLast("xml-decoder",new HttpXmlRequestDecoder(Order.class,true));
                            //HttpResponseEncoder 将应答消息编码为HTTP消息
                            socketChannel.pipeline()
                                    .addLast("http-encoder",new HttpResponseEncoder());
                            //HttpXmlResponseEncoder 将HTTP 实现HTTP+XML请求消息的自动编码
                            socketChannel.pipeline()
                                    .addLast("xml-encoder",new HttpXmlResponseEncoder());
                            //业务逻辑处理类
                            socketChannel.pipeline()
                                    .addLast(new HttpXmlServerHandler());
                        }
                    });
            ChannelFuture future=bootstrap.bind(port).sync();
            System.out.println("HTTP-XML Server is started,waiting for client to connect..");
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            workerGroup.shutdownGracefully();
            boosGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new HttpXmlServer().run(8080);
    }

}
public class HttpXmlServerHandler extends SimpleChannelInboundHandler<HttpXmlRequest> {
    @Override
    protected void messageReceived(ChannelHandlerContext channelHandlerContext, HttpXmlRequest httpXmlRequest) throws Exception {
       //可以看出服务端业务逻辑处理类接收到的已经是解码后的业务消息
        HttpRequest request=httpXmlRequest.getRequest();
        Order order=(Order) httpXmlRequest.getBody();
        System.out.println("Http server receive request : "+order);
        //修改order
       // doBusiness(order);
        //发送修改后的order 数据
      //  ChannelFuture future=
        channelHandlerContext.
                writeAndFlush(new HttpXmlResponse(null,order));/*.
                addListener(ChannelFutureListener.CLOSE);*/
        System.out.println("server  send data complete..");
        //if ()
    }

    private void doBusiness(Order order){
        order.getCustomer().setFirstName("张");
        order.getCustomer().setLastName("飞");
        List<String> midNames=new ArrayList<String >();
        midNames.add("关羽");
        midNames.add("刘备");
        order.getCustomer().setMiddleNames(midNames);

       order.getBilTo().setCity("荆州");
       order.getBilTo().setCountry("东汉末年");
        order.getBilTo().setState("war");
        order.getBilTo().setPostCode("654321");


        order.getShipTo().setCity("荆州");
        order.getShipTo().setCountry("东汉末年");
        order.getShipTo().setState("war");
        order.getShipTo().setPostCode("654321");
    }

    public void exceptionCaught(ChannelHandlerContext context,Throwable cause){
        if (context.channel().isActive()){
            sendError(context,HttpResponseStatus.INTERNAL_SERVER_ERROR);
        }
    }

    private  void sendError(ChannelHandlerContext context, HttpResponseStatus status){
        //构造错误返回体
        FullHttpResponse response=new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
                status, Unpooled.copiedBuffer("失败: "+status.toString()+"\r\n",
                CharsetUtil.UTF_8));
        response.headers().set(CONTENT_TYPE,"text/plain; charset=UTF-8");
        context.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }
}

编码解码基类

public abstract class AbstractHttpXmlDecoder<T> extends MessageToMessageDecoder<T> {
    private IBindingFactory factory;
    private StringReader reader;
    //解码对象类型
    private Class<?> clazz;
    //是否打印消息体码流的开关,默认关闭
    private boolean isPrint;
    private final static String CHARSET_NAME = "UTF-8";
    private final static Charset UTF_8 = Charset.forName(CHARSET_NAME);


    protected AbstractHttpXmlDecoder(Class<?> clazz) {
        this(clazz, false);
    }

    protected AbstractHttpXmlDecoder(Class<?> clazz, boolean isPrint) {
        this.clazz = clazz;
        this.isPrint = isPrint;
    }

    protected Object decode0(ChannelHandlerContext context, ByteBuf body) throws JiBXException {
        factory = BindingDirectory.getFactory(clazz);
        //从消息体中获取码流
        String content = body.toString(UTF_8);
        //通过isPrint 来判断是否打印消息体,主要是为了方便定位问题
        if (isPrint) {
            System.out.println("The body is : " + content);
        }

        reader = new StringReader(content);
        //通过JiBx 类库将XML转换为POJO对象。
        IUnmarshallingContext unmarshallingContext = factory.createUnmarshallingContext();
        Object result = unmarshallingContext.unmarshalDocument(reader);
        //释放资源
        reader.close();
        reader = null;
        return result;
    }


    public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
        //释放资源
        if (reader != null) {
            reader.close();
            reader = null;
        }
    }

}
public abstract class AbstractHttpXmlEncoder<T> extends MessageToMessageEncoder<T> {

    IBindingFactory factory = null;
    StringWriter writer = null;
    final static String CHARSET_NAME = "UTF-8";
    final static Charset UTF_8 = Charset.forName(CHARSET_NAME);


    protected ByteBuf encode0(ChannelHandlerContext contextReq, Object body) throws JiBXException, IOException {
        factory = BindingDirectory.getFactory(body.getClass());
        writer = new StringWriter();
        IMarshallingContext context = factory.createMarshallingContext();
        context.setIndent(2);
        //将业务POJO(Order)类实例化为XML字符串。
        context.marshalDocument(body, CHARSET_NAME, null, writer);
        String xmlStr = writer.toString();
        writer.close();
        writer = null;
        //将字符串包装成Netty的ByteBuf并返回,实现了HTTP请求消息的XML编码
        ByteBuf encodeBuf = Unpooled.copiedBuffer(xmlStr, UTF_8);
        return encodeBuf;
    }

    public void exceptionCaught(ChannelHandlerContext context, Throwable throwable) throws IOException {
        //释放资源
        if (writer != null) {
            writer.close();
            writer = null;
        }
    }
}

代码说明

这里自定义开发的HTTP-XML 的编码器和解码器。主要是用到了 JiBX技术。它很方便实现XML和POJO对象的转变。
如果不清楚JiBX。可以查看博客学习。
https://blog.csdn.net/echohuangshihuxue/article/details/128653428

测试

服务端打印结果

在这里插入图片描述
成功的解码了客户端发送过来的HTTP-XML 消息并解码为POJO对象进行了打印。 然后构造HTTP-XML 消息,编码后发送给客户端。

客户端打印结果

在这里插入图片描述
客户端能成功的解码服务端发送过来的HTTP-XML消息。说明服务端的编码和客户端的解码都成功了。

目前代码里面注释写的比较详细了。大家可以先敲敲实现功能。如果还有疑问,可以对照开发流程图查看会比较清楚。
在这里插入图片描述

总结

本章重点介绍了如何利用Netty的HTTP协议栈开发基于HTTP的应用程序。尽管HTTP+XML协议栈是个高性能,通用的协议栈,但是,我们忽略一些细节,比如异常封装等。大家可以自己完善。如果有其他问题,欢迎讨论。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值